Skip to content

Commit 003db38

Browse files
committed
Use custom footer with links, custom font and static logo in the header
1 parent 163fda4 commit 003db38

File tree

10 files changed

+259
-105
lines changed

10 files changed

+259
-105
lines changed

components/Footer.tsx

Lines changed: 140 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,10 @@ import * as config from '@/lib/config'
1313
import { useDarkMode } from '@/lib/use-dark-mode'
1414

1515
import styles from './styles.module.css'
16+
import { StaticLogo } from './StaticLogo'
17+
import { footerLinks } from '@/lib/config'
18+
import { useNotionContext } from 'react-notion-x'
19+
import cs from 'classnames'
1620

1721
// TODO: merge the data and icons from PageSocial with the social links in Footer
1822

@@ -33,109 +37,145 @@ export function FooterImpl() {
3337
setHasMounted(true)
3438
}, [])
3539

40+
const { components, mapPageUrl } = useNotionContext()
41+
3642
return (
3743
<footer className={styles.footer}>
38-
<div className={styles.copyright}>
39-
Copyright {currentYear} {config.author}
40-
</div>
41-
42-
<div className={styles.settings}>
43-
{hasMounted && (
44-
<a
45-
className={styles.toggleDarkMode}
46-
href='#'
47-
role='button'
48-
onClick={onToggleDarkMode}
49-
title='Toggle dark mode'
50-
>
51-
{isDarkMode ? <IoMoonSharp /> : <IoSunnyOutline />}
52-
</a>
53-
)}
44+
<div className={styles.footerSocial}>
45+
<StaticLogo />
46+
47+
<div className={cs(styles.settings,styles.toggleDarkModeContainer)}>
48+
{hasMounted && (
49+
<a
50+
className={styles.toggleDarkMode}
51+
href='#'
52+
role='button'
53+
onClick={onToggleDarkMode}
54+
title='Toggle dark mode'
55+
>
56+
{isDarkMode ? <IoMoonSharp /> : <IoSunnyOutline />}
57+
</a>
58+
)}
59+
</div>
60+
61+
<div className={styles.social}>
62+
{config.twitter && (
63+
<a
64+
className={styles.twitter}
65+
href={`https://twitter.com/${config.twitter}`}
66+
title={`Twitter @${config.twitter}`}
67+
target='_blank'
68+
rel='noopener noreferrer'
69+
>
70+
<FaTwitter />
71+
</a>
72+
)}
73+
74+
{config.mastodon && (
75+
<a
76+
className={styles.mastodon}
77+
href={config.mastodon}
78+
title={`Mastodon ${config.getMastodonHandle()}`}
79+
rel='me'
80+
>
81+
<FaMastodon />
82+
</a>
83+
)}
84+
85+
{config.zhihu && (
86+
<a
87+
className={styles.zhihu}
88+
href={`https://zhihu.com/people/${config.zhihu}`}
89+
title={`Zhihu @${config.zhihu}`}
90+
target='_blank'
91+
rel='noopener noreferrer'
92+
>
93+
<FaZhihu />
94+
</a>
95+
)}
96+
97+
{config.github && (
98+
<a
99+
className={styles.github}
100+
href={`https://github.com/${config.github}`}
101+
title={`GitHub @${config.github}`}
102+
target='_blank'
103+
rel='noopener noreferrer'
104+
>
105+
<FaGithub />
106+
</a>
107+
)}
108+
109+
{config.linkedin && (
110+
<a
111+
className={styles.linkedin}
112+
href={`https://www.linkedin.com/in/${config.linkedin}`}
113+
title={`LinkedIn ${config.author}`}
114+
target='_blank'
115+
rel='noopener noreferrer'
116+
>
117+
<FaLinkedin />
118+
</a>
119+
)}
120+
121+
{config.newsletter && (
122+
<a
123+
className={styles.newsletter}
124+
href={`${config.newsletter}`}
125+
title={`Newsletter ${config.author}`}
126+
target='_blank'
127+
rel='noopener noreferrer'
128+
>
129+
<FaEnvelopeOpenText />
130+
</a>
131+
)}
132+
133+
{config.youtube && (
134+
<a
135+
className={styles.youtube}
136+
href={`https://www.youtube.com/${config.youtube}`}
137+
title={`YouTube ${config.author}`}
138+
target='_blank'
139+
rel='noopener noreferrer'
140+
>
141+
<FaYoutube />
142+
</a>
143+
)}
144+
</div>
54145
</div>
55-
56-
<div className={styles.social}>
57-
{config.twitter && (
58-
<a
59-
className={styles.twitter}
60-
href={`https://twitter.com/${config.twitter}`}
61-
title={`Twitter @${config.twitter}`}
62-
target='_blank'
63-
rel='noopener noreferrer'
64-
>
65-
<FaTwitter />
66-
</a>
67-
)}
68-
69-
{config.mastodon && (
70-
<a
71-
className={styles.mastodon}
72-
href={config.mastodon}
73-
title={`Mastodon ${config.getMastodonHandle()}`}
74-
rel='me'
75-
>
76-
<FaMastodon />
77-
</a>
78-
)}
79-
80-
{config.zhihu && (
81-
<a
82-
className={styles.zhihu}
83-
href={`https://zhihu.com/people/${config.zhihu}`}
84-
title={`Zhihu @${config.zhihu}`}
85-
target='_blank'
86-
rel='noopener noreferrer'
87-
>
88-
<FaZhihu />
89-
</a>
90-
)}
91-
92-
{config.github && (
93-
<a
94-
className={styles.github}
95-
href={`https://github.com/${config.github}`}
96-
title={`GitHub @${config.github}`}
97-
target='_blank'
98-
rel='noopener noreferrer'
99-
>
100-
<FaGithub />
101-
</a>
102-
)}
103-
104-
{config.linkedin && (
105-
<a
106-
className={styles.linkedin}
107-
href={`https://www.linkedin.com/in/${config.linkedin}`}
108-
title={`LinkedIn ${config.author}`}
109-
target='_blank'
110-
rel='noopener noreferrer'
111-
>
112-
<FaLinkedin />
113-
</a>
114-
)}
115-
116-
{config.newsletter && (
117-
<a
118-
className={styles.newsletter}
119-
href={`${config.newsletter}`}
120-
title={`Newsletter ${config.author}`}
121-
target='_blank'
122-
rel='noopener noreferrer'
123-
>
124-
<FaEnvelopeOpenText />
125-
</a>
126-
)}
127-
128-
{config.youtube && (
129-
<a
130-
className={styles.youtube}
131-
href={`https://www.youtube.com/${config.youtube}`}
132-
title={`YouTube ${config.author}`}
133-
target='_blank'
134-
rel='noopener noreferrer'
135-
>
136-
<FaYoutube />
137-
</a>
138-
)}
146+
<div className="SiteInfo">
147+
<div className={styles.siteInfoLinks}>
148+
{footerLinks
149+
?.map((link, index) => {
150+
if (!link.pageId && !link.url) {
151+
return null
152+
}
153+
154+
if (link.pageId) {
155+
return (
156+
<components.PageLink
157+
href={mapPageUrl(link.pageId)}
158+
key={index}
159+
className={cs(styles.navLink, 'breadcrumb', 'button')}
160+
>
161+
{link.title}
162+
</components.PageLink>
163+
)
164+
} else {
165+
return (
166+
<components.Link
167+
href={link.url}
168+
key={index}
169+
className={cs(styles.navLink, 'breadcrumb', 'button')}
170+
>
171+
{link.title}
172+
</components.Link>
173+
)
174+
}
175+
})
176+
.filter(Boolean)}
177+
</div>
178+
<div className={styles.copyright}>© Copyright {currentYear}. All rights reserved.</div>
139179
</div>
140180
</footer>
141181
)

components/NotionPageHeader.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@ import { isSearchEnabled, navigationLinks, navigationStyle } from '@/lib/config'
99
import { useDarkMode } from '@/lib/use-dark-mode'
1010

1111
import styles from './styles.module.css'
12+
import { StaticLogo } from './StaticLogo'
1213

1314
function ToggleThemeButton() {
1415
const [hasMounted, setHasMounted] = React.useState(false)
@@ -46,8 +47,9 @@ export function NotionPageHeader({
4647
return (
4748
<header className='notion-header'>
4849
<div className='notion-nav-header'>
49-
<Breadcrumbs block={block} rootOnly={true} />
50-
50+
<div className="notion-nav-header-rhs breadcrumbs">
51+
<StaticLogo />
52+
</div>
5153
<div className='notion-nav-header-rhs breadcrumbs'>
5254
{navigationLinks
5355
?.map((link, index) => {

components/StaticLogo.module.css

Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
.logo {
2+
height: 75%;
3+
margin-right: 5px;
4+
border-radius: 50%;
5+
display: flex;
6+
}
7+
8+
.logo img {
9+
max-height: 50px;
10+
max-width: 50px;
11+
object-fit: contain;
12+
}
13+
.logo > * {
14+
display: inline-flex;
15+
font-weight: 500;
16+
padding-right: 0.1em;
17+
font-size: 2em;
18+
align-items: center;
19+
justify-content: center;
20+
white-space: nowrap;
21+
text-overflow: ellipsis;
22+
}
23+
24+
@media only screen and (max-width: 660px) {
25+
.logo {
26+
justify-content: center;
27+
}
28+
}

components/StaticLogo.tsx

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import * as config from '@/lib/config'
2+
import cs from 'classnames'
3+
4+
import styles from './StaticLogo.module.css'
5+
6+
export function StaticLogo() {
7+
return (
8+
<a className={cs(styles.logo, styles.link)} href={config.host} rel="home" title="Logo">
9+
<img src="/favicon.png"/>
10+
<span>{config.name}</span>
11+
</a>
12+
)
13+
}

components/styles.module.css

Lines changed: 36 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -49,16 +49,31 @@
4949
max-width: 1100px;
5050
margin: auto auto 0;
5151
padding: 8px;
52+
}
5253

54+
.footerSocial {
5355
display: flex;
5456
flex-direction: row;
5557
justify-content: space-between;
5658
align-items: center;
5759
}
5860

61+
.siteInfoLinks {
62+
display: flex;
63+
flex-direction: row;
64+
justify-content: center;
65+
align-items: center;
66+
}
67+
68+
.siteInfoLinks a {
69+
margin: 0.5em;
70+
text-align: center;
71+
}
72+
5973
.copyright {
6074
font-size: 80%;
6175
padding: 0.5em;
76+
text-align: center;
6277
}
6378

6479
.settings,
@@ -124,11 +139,31 @@
124139
border-top: 1px solid var(--fg-color-0);
125140
}
126141

127-
@media only screen and (max-width: 566px) {
142+
@media only screen and (min-width: 661px) {
143+
/* Position the dark mode / light mode button in the middle of the screen no matter what */
144+
.footer .toggleDarkModeContainer {
145+
position: absolute;
146+
left: 50%;
147+
transform: translateX(-50%);
148+
z-index: 1; /* Ensure the centered item overlays the others */
149+
}
150+
}
151+
152+
@media only screen and (max-width: 660px) {
128153
.footer {
129154
flex-direction: column;
130155
}
131156

157+
.footer .footerSocial {
158+
flex-direction: column-reverse;
159+
}
160+
161+
.footer .link {
162+
display: flex;
163+
align-items: center;
164+
justify-content: center;
165+
}
166+
132167
.footer > div {
133168
margin-top: 1em;
134169
}

lib/config.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,11 @@ export const navigationLinks: Array<NavigationLink | null> = getSiteConfig(
109109
null
110110
)
111111

112+
export const footerLinks: Array<NavigationLink | null> = getSiteConfig(
113+
'footerLinks',
114+
null
115+
)
116+
112117
// Optional site search
113118
export const isSearchEnabled: boolean = getSiteConfig('isSearchEnabled', true)
114119

0 commit comments

Comments
 (0)