aboutsummaryrefslogtreecommitdiffstats
path: root/src
diff options
context:
space:
mode:
authorLibravatar Kristóf Marussy <kristof@marussy.com>2024-03-23 18:02:45 +0100
committerLibravatar Kristóf Marussy <kristof@marussy.com>2024-03-23 18:07:21 +0100
commitfbd41d395916176dde11bb0e417f1210f34eb4ab (patch)
tree7384a50b1a90ba5909d8f93f589df661640681e6 /src
parentSimplify compression (diff)
downloadblog-fbd41d395916176dde11bb0e417f1210f34eb4ab.tar.gz
blog-fbd41d395916176dde11bb0e417f1210f34eb4ab.tar.zst
blog-fbd41d395916176dde11bb0e417f1210f34eb4ab.zip
Add blog
Site structure follows SEO tips from https://johnnyreilly.com/how-we-fixed-my-seo * The blog pages have as simple of an URL as possible. To this end, the home page of the site is actually the first index page of the blog. * Customize the blog index page BlogListPage component to show the landing page as the first index page. * Rename /archive to /blog to avoid a dated feel. * Remove the date from post URLs using the slug property.
Diffstat (limited to 'src')
-rw-r--r--src/components/ActiveSectionProvider.tsx2
-rw-r--r--src/components/Landing.tsx31
-rw-r--r--src/components/landing/sections/Blog.module.css28
-rw-r--r--src/components/landing/sections/Blog.tsx95
-rw-r--r--src/components/landing/sections/Hero.module.css10
-rw-r--r--src/css/custom.css8
-rw-r--r--src/pages/index.tsx28
-rw-r--r--src/plugins/compressionPlugin.ts10
-rw-r--r--src/theme/BlogListPage.tsx18
-rw-r--r--src/theme/BlogSidebar/Desktop.module.css53
-rw-r--r--src/theme/BlogSidebar/Desktop.tsx55
-rw-r--r--src/theme/Footer/Copyright.tsx (renamed from src/theme/Footer/Copyright.jsx)5
-rw-r--r--src/theme/NavbarItem/NavbarNavLink.tsx (renamed from src/theme/NavbarItem/NavbarNavLink.jsx)4
-rw-r--r--src/theme/PaginatorNavLink.tsx17
-rw-r--r--src/theme/Root.tsx11
-rw-r--r--src/theme/SiteMetadata.jsx28
16 files changed, 335 insertions, 68 deletions
diff --git a/src/components/ActiveSectionProvider.tsx b/src/components/ActiveSectionProvider.tsx
index 022dbad..0d491ac 100644
--- a/src/components/ActiveSectionProvider.tsx
+++ b/src/components/ActiveSectionProvider.tsx
@@ -46,7 +46,7 @@ export default function ActiveSectionProvider({
46 if (hash === lastHashRef.current) { 46 if (hash === lastHashRef.current) {
47 return; 47 return;
48 } 48 }
49 // Passing `undefined` to `hash` will give contraol pack to react-router. 49 // Passing `undefined` to `hash` will give control back to react-router.
50 const newURL = `${location.pathname}${location.search}${ 50 const newURL = `${location.pathname}${location.search}${
51 hash ?? location.hash 51 hash ?? location.hash
52 }`; 52 }`;
diff --git a/src/components/Landing.tsx b/src/components/Landing.tsx
new file mode 100644
index 0000000..5eac99b
--- /dev/null
+++ b/src/components/Landing.tsx
@@ -0,0 +1,31 @@
1/*
2 * SPDX-FileCopyrightText: 2023 Kristóf Marussy
3 *
4 * SPDX-License-Identifier: MIT
5 */
6
7import type { Props } from '@theme/BlogListPage';
8import Layout from '@theme/Layout';
9
10import Blog from './landing/sections/Blog';
11import Contact from './landing/sections/Contact';
12import Hero from './landing/sections/Hero';
13import Publications from './landing/sections/Publications';
14import Research from './landing/sections/Research';
15import Resume from './landing/sections/Resume';
16import TrackActiveSection from './TrackActiveSection';
17
18export default function Home(props: Props) {
19 return (
20 <Layout>
21 <TrackActiveSection>
22 <Hero />
23 <Research />
24 <Blog {...props} />
25 <Publications />
26 <Resume />
27 <Contact />
28 </TrackActiveSection>
29 </Layout>
30 );
31}
diff --git a/src/components/landing/sections/Blog.module.css b/src/components/landing/sections/Blog.module.css
new file mode 100644
index 0000000..6a38a97
--- /dev/null
+++ b/src/components/landing/sections/Blog.module.css
@@ -0,0 +1,28 @@
1/*
2 * SPDX-FileCopyrightText: 2024 Kristóf Marussy
3 *
4 * SPDX-License-Identifier: MIT
5 */
6
7.row {
8 margin-bottom: var(--ifm-paragraph-margin-bottom);
9}
10
11.recent-posts {
12 margin-bottom: 0;
13 font-size: var(--ifm-h3-font-size);
14 font-weight: var(--ifm-font-weight-bold);
15 --casl: 0.5;
16 letter-spacing: var(--marussy-heading-tracking);
17}
18
19.date {
20 font-size: 1rem;
21 font-weight: var(--ifm-font-weight-normal);
22 --casl: 0;
23 letter-spacing: 0;
24}
25
26.prev::after {
27 content: ' »';
28}
diff --git a/src/components/landing/sections/Blog.tsx b/src/components/landing/sections/Blog.tsx
new file mode 100644
index 0000000..3ac87db
--- /dev/null
+++ b/src/components/landing/sections/Blog.tsx
@@ -0,0 +1,95 @@
1/*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 * Copyright (c) 2024 Kristóf Marussy <kristof@marussy.com>
4 *
5 * SPDX-License-Identifier: MIT
6 */
7
8import Link from '@docusaurus/Link';
9import type { Props } from '@theme/BlogListPage';
10import type { Content } from '@theme/BlogPostPage';
11import Translate from '@docusaurus/Translate';
12import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
13import clsx from 'clsx';
14
15import Section from '@site/src/components/landing/Section';
16import Subtitle from '@site/src/components/landing/Subtitle';
17
18import styles from './Blog.module.css';
19import React from 'react';
20
21const columnLength = 5;
22
23function Column({
24 items,
25}: {
26 items: { readonly content: Content }[];
27}): React.ReactNode {
28 // Date time format based on
29 // https://github.com/facebook/docusaurus/blob/6f17d5493877ba38d8b4e0b0d468f44401375c30/packages/docusaurus-theme-common/src/utils/IntlUtils.ts
30 const {
31 i18n: { currentLocale, localeConfigs },
32 } = useDocusaurusContext();
33 const calendar = localeConfigs[currentLocale]!.calendar;
34 const dateTimeFormat = new Intl.DateTimeFormat(currentLocale, {
35 calendar,
36 day: 'numeric',
37 month: 'long',
38 year: 'numeric',
39 timeZone: 'UTC',
40 });
41
42 if (items.length === 0) {
43 return null;
44 }
45
46 return (
47 <div className="col col-6">
48 <ul className={styles['recent-posts']}>
49 {items.map(({ content }) => (
50 <li key={content.metadata.date}>
51 <Link to={content.metadata.permalink}>
52 {content.metadata.title}
53 </Link>{' '}
54 <span className={styles.date}>
55 on&nbsp;{dateTimeFormat.format(new Date(content.metadata.date))}
56 </span>
57 </li>
58 ))}
59 </ul>
60 </div>
61 );
62}
63
64export default function Blog(props: Props) {
65 const {
66 items,
67 metadata: { nextPage },
68 } = props;
69 const columnLength = Math.max(1, Math.ceil(items.length / 2));
70 return (
71 <Section id="blog" title="Blog">
72 <div className="container">
73 <section>
74 <Subtitle icon="🗓️">Recent posts</Subtitle>
75 </section>
76 <div className={clsx('row', styles.row)}>
77 <Column items={items.slice(0, columnLength)} />
78 <Column items={items.slice(columnLength)} />
79 </div>
80 {nextPage && (
81 <p>
82 <Link to={nextPage} className={styles.prev}>
83 <Translate
84 id="theme.blog.paginator.olderEntries"
85 description="The label used to navigate to the older blog posts page (next page)"
86 >
87 Older Entries
88 </Translate>
89 </Link>
90 </p>
91 )}
92 </div>
93 </Section>
94 );
95}
diff --git a/src/components/landing/sections/Hero.module.css b/src/components/landing/sections/Hero.module.css
index 67e7d54..0d0e16e 100644
--- a/src/components/landing/sections/Hero.module.css
+++ b/src/components/landing/sections/Hero.module.css
@@ -11,7 +11,7 @@
11 --ifm-link-hover-color: var(--ifm-link-color); 11 --ifm-link-hover-color: var(--ifm-link-color);
12} 12}
13 13
14@media (max-width: 996px) { 14@media (max-width: 576px) {
15 .hero { 15 .hero {
16 padding-top: 2rem; 16 padding-top: 2rem;
17 padding-bottom: 0; 17 padding-bottom: 0;
@@ -65,11 +65,17 @@
65 font-family: var(--ifm-heading-font-family); 65 font-family: var(--ifm-heading-font-family);
66 font-weight: var(--ifm-heading-font-weight); 66 font-weight: var(--ifm-heading-font-weight);
67 --casl: 1; 67 --casl: 1;
68 font-size: 2.5rem; 68 font-size: 3rem;
69 letter-spacing: var(--marussy-heading-tracking); 69 letter-spacing: var(--marussy-heading-tracking);
70 white-space: pre; 70 white-space: pre;
71} 71}
72 72
73@media (max-width: 576px) {
74 .introduction__name {
75 font-size: 2.5rem;
76 }
77}
78
73.cta { 79.cta {
74 display: flex; 80 display: flex;
75 flex-direction: row; 81 flex-direction: row;
diff --git a/src/css/custom.css b/src/css/custom.css
index c2cda19..bd6e179 100644
--- a/src/css/custom.css
+++ b/src/css/custom.css
@@ -84,11 +84,13 @@ kbd {
84 84
85h1, 85h1,
86h2, 86h2,
87.markdown > h3,
87.button { 88.button {
88 --casl: 1; 89 --casl: 1;
89} 90}
90 91
91h3 { 92h3,
93.markdown > h4 {
92 --casl: 0.5; 94 --casl: 0.5;
93} 95}
94 96
@@ -107,3 +109,7 @@ h3 {
107 --casl: 0; 109 --casl: 0;
108 --mono: 1; 110 --mono: 1;
109} 111}
112
113.hero__subtitle {
114 font-weight: 500;
115}
diff --git a/src/pages/index.tsx b/src/pages/index.tsx
deleted file mode 100644
index 60f86ca..0000000
--- a/src/pages/index.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2023 Kristóf Marussy
3 *
4 * SPDX-License-Identifier: MIT
5 */
6
7import Layout from '@theme/Layout';
8
9import Contact from '@site/src/components/landing/sections/Contact';
10import Hero from '@site/src/components/landing/sections/Hero';
11import Publications from '@site/src/components/landing/sections/Publications';
12import Research from '@site/src/components/landing/sections/Research';
13import Resume from '@site/src/components/landing/sections/Resume';
14import TrackActiveSection from '@site/src/components/TrackActiveSection';
15
16export default function Home() {
17 return (
18 <Layout>
19 <TrackActiveSection>
20 <Hero />
21 <Research />
22 <Publications />
23 <Resume />
24 <Contact />
25 </TrackActiveSection>
26 </Layout>
27 );
28}
diff --git a/src/plugins/compressionPlugin.ts b/src/plugins/compressionPlugin.ts
index 7b9feeb..a5bc402 100644
--- a/src/plugins/compressionPlugin.ts
+++ b/src/plugins/compressionPlugin.ts
@@ -20,10 +20,9 @@ const test = /\.(js|css|svg|txt)$/;
20export default function compressionPlugin(): Plugin<void> { 20export default function compressionPlugin(): Plugin<void> {
21 return { 21 return {
22 name: 'marussy-compression-plugin', 22 name: 'marussy-compression-plugin',
23 configureWebpack: (_config, isServer) => 23 configureWebpack: ({ mode }) =>
24 isServer 24 mode === 'production'
25 ? {} 25 ? {
26 : {
27 plugins: [ 26 plugins: [
28 new CompressionPlugin({ 27 new CompressionPlugin({
29 test, 28 test,
@@ -39,6 +38,7 @@ export default function compressionPlugin(): Plugin<void> {
39 minRatio, 38 minRatio,
40 }), 39 }),
41 ], 40 ],
42 }, 41 }
42 : {},
43 }; 43 };
44} 44}
diff --git a/src/theme/BlogListPage.tsx b/src/theme/BlogListPage.tsx
new file mode 100644
index 0000000..b8c7e01
--- /dev/null
+++ b/src/theme/BlogListPage.tsx
@@ -0,0 +1,18 @@
1/*
2 * SPDX-FileCopyrightText: 2024 Kristóf Marussy
3 *
4 * SPDX-License-Identifier: MIT
5 */
6
7import React from 'react';
8import type { Props } from '@theme/BlogListPage';
9import BlogListPage from '@theme-original/BlogListPage';
10
11import Landing from '@site/src/components/Landing';
12
13export default function BlogListPageWrapper(props: Props) {
14 if (props.metadata.permalink === '/') {
15 return <Landing {...props} />;
16 }
17 return <BlogListPage {...props} />;
18}
diff --git a/src/theme/BlogSidebar/Desktop.module.css b/src/theme/BlogSidebar/Desktop.module.css
new file mode 100644
index 0000000..4bac5ba
--- /dev/null
+++ b/src/theme/BlogSidebar/Desktop.module.css
@@ -0,0 +1,53 @@
1/*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 * Copyright (c) 2024 Kristóf Marussy <kristof@marussy.com>
4 *
5 * SPDX-License-Identifier: MIT
6 *
7 * This file was derived from
8 * https://github.com/facebook/docusaurus/blob/6f17d5493877ba38d8b4e0b0d468f44401375c30/packages/docusaurus-theme-classic/src/theme/BlogSidebar/Desktop/styles.module.css
9 * via the `swizzle` mechanism of Docusaurus.
10 *
11 * It was modified to change the sidebar title styling.
12 */
13
14.sidebar {
15 max-height: calc(100vh - (var(--ifm-navbar-height) + 2rem));
16 overflow-y: auto;
17 position: sticky;
18 top: calc(var(--ifm-navbar-height) + 2rem);
19}
20
21.sidebarItemTitle {
22 font-size: 1.5rem;
23 font-weight: var(--ifm-font-weight-bold);
24 --casl: 1;
25 letter-spacing: var(--marussy-heading-tracking);
26}
27
28.sidebarItemList {
29 font-size: 0.9rem;
30}
31
32.sidebarItem {
33 margin-top: 0.7rem;
34}
35
36.sidebarItemLink {
37 color: var(--ifm-font-color-base);
38 display: block;
39}
40
41.sidebarItemLink:hover {
42 text-decoration: none;
43}
44
45.sidebarItemLinkActive {
46 color: var(--ifm-color-primary) !important;
47}
48
49@media (max-width: 996px) {
50 .sidebar {
51 display: none;
52 }
53}
diff --git a/src/theme/BlogSidebar/Desktop.tsx b/src/theme/BlogSidebar/Desktop.tsx
new file mode 100644
index 0000000..d06f239
--- /dev/null
+++ b/src/theme/BlogSidebar/Desktop.tsx
@@ -0,0 +1,55 @@
1/*
2 * Copyright (c) Facebook, Inc. and its affiliates.
3 * Copyright (c) 2024 Kristóf Marussy <kristof@marussy.com>
4 *
5 * SPDX-License-Identifier: MIT
6 *
7 * This file was derived from
8 * https://github.com/facebook/docusaurus/blob/6f17d5493877ba38d8b4e0b0d468f44401375c30/packages/docusaurus-theme-classic/src/theme/BlogSidebar/Desktop/index.tsx
9 * via the `swizzle` mechanism of Docusaurus.
10 *
11 * It was modified to change the sidebar title styling.
12 */
13
14import Link from '@docusaurus/Link';
15import { translate } from '@docusaurus/Translate';
16import { useVisibleBlogSidebarItems } from '@docusaurus/theme-common/internal';
17import type { Props } from '@theme/BlogSidebar/Desktop';
18import React from 'react';
19import clsx from 'clsx';
20
21import styles from './Desktop.module.css';
22
23export default function BlogSidebarDesktop({ sidebar }: Props) {
24 const items = useVisibleBlogSidebarItems(sidebar.items);
25 return (
26 <aside className="col col--3">
27 <nav
28 className={clsx(styles.sidebar, 'thin-scrollbar')}
29 aria-label={translate({
30 id: 'theme.blog.sidebar.navAriaLabel',
31 message: 'Blog recent posts navigation',
32 description: 'The ARIA label for recent posts in the blog sidebar',
33 })}
34 >
35 <div className={clsx(styles.sidebarItemTitle, 'margin-bottom--md')}>
36 <span aria-hidden="true">🗓️</span> {sidebar.title}
37 </div>
38 <ul className={clsx(styles.sidebarItemList, 'clean-list')}>
39 {items.map((item) => (
40 <li key={item.permalink} className={styles.sidebarItem}>
41 <Link
42 isNavLink
43 to={item.permalink}
44 className={styles.sidebarItemLink}
45 activeClassName={styles.sidebarItemLinkActive}
46 >
47 {item.title}
48 </Link>
49 </li>
50 ))}
51 </ul>
52 </nav>
53 </aside>
54 );
55}
diff --git a/src/theme/Footer/Copyright.jsx b/src/theme/Footer/Copyright.tsx
index 648bd42..3125c05 100644
--- a/src/theme/Footer/Copyright.jsx
+++ b/src/theme/Footer/Copyright.tsx
@@ -5,13 +5,14 @@
5 * SPDX-License-Identifier: MIT 5 * SPDX-License-Identifier: MIT
6 * 6 *
7 * This file was derived from 7 * This file was derived from
8 * https://github.com/facebook/docusaurus/blob/c745021b01a8b88d34e1d772278d7171ad8acdf5/packages/docusaurus-theme-classic/src/theme/Footer/Copyright/index.tsx 8 * https://github.com/facebook/docusaurus/blob/cc0bceab9c1678303f6237f5526753edc1b12fc3/packages/docusaurus-theme-classic/src/theme/Footer/Copyright/index.tsx
9 * via the `swizzle` mechanism of Docusaurus. 9 * via the `swizzle` mechanism of Docusaurus.
10 * 10 *
11 * It was modified to embed JSX content directly in the footer. 11 * It was modified to embed JSX content directly in the footer.
12 */ 12 */
13 13
14import Link from '@docusaurus/Link'; 14import Link from '@docusaurus/Link';
15import type { Props } from '@theme/Footer/Copyright';
15import clsx from 'clsx'; 16import clsx from 'clsx';
16import React from 'react'; 17import React from 'react';
17 18
@@ -19,7 +20,7 @@ import { CCLicenseLink, LicenseLink } from '@site/src/components/licenses';
19 20
20import styles from './Copyright.module.css'; 21import styles from './Copyright.module.css';
21 22
22export default function FooterCopyright({ copyright }) { 23export default function FooterCopyright({ copyright }: Props) {
23 return ( 24 return (
24 <div className="footer__copyright"> 25 <div className="footer__copyright">
25 Most content on this site is{' '} 26 Most content on this site is{' '}
diff --git a/src/theme/NavbarItem/NavbarNavLink.jsx b/src/theme/NavbarItem/NavbarNavLink.tsx
index 81de931..a9d7b04 100644
--- a/src/theme/NavbarItem/NavbarNavLink.jsx
+++ b/src/theme/NavbarItem/NavbarNavLink.tsx
@@ -18,6 +18,8 @@ import useBaseUrl from '@docusaurus/useBaseUrl';
18import isInternalUrl from '@docusaurus/isInternalUrl'; 18import isInternalUrl from '@docusaurus/isInternalUrl';
19import { isRegexpStringMatch } from '@docusaurus/theme-common'; 19import { isRegexpStringMatch } from '@docusaurus/theme-common';
20import IconExternalLink from '@theme/Icon/ExternalLink'; 20import IconExternalLink from '@theme/Icon/ExternalLink';
21import type { Props } from '@theme/NavbarItem/NavbarNavLink';
22
21import { useActiveSection } from '@site/src/components/ActiveSectionProvider'; 23import { useActiveSection } from '@site/src/components/ActiveSectionProvider';
22 24
23export default function NavbarNavLink({ 25export default function NavbarNavLink({
@@ -30,7 +32,7 @@ export default function NavbarNavLink({
30 isDropdownLink, 32 isDropdownLink,
31 prependBaseUrlToHref, 33 prependBaseUrlToHref,
32 ...props 34 ...props
33}) { 35}: Props) {
34 const { hash: activeSectionHash } = useActiveSection(); 36 const { hash: activeSectionHash } = useActiveSection();
35 // TODO all this seems hacky 37 // TODO all this seems hacky
36 // {to: 'version'} should probably be forbidden, in favor of {to: '/version'} 38 // {to: 'version'} should probably be forbidden, in favor of {to: '/version'}
diff --git a/src/theme/PaginatorNavLink.tsx b/src/theme/PaginatorNavLink.tsx
new file mode 100644
index 0000000..d0f03f7
--- /dev/null
+++ b/src/theme/PaginatorNavLink.tsx
@@ -0,0 +1,17 @@
1/*
2 * SPDX-FileCopyrightText: 2024 Kristóf Marussy
3 *
4 * SPDX-License-Identifier: MIT
5 */
6
7import React from 'react';
8import type { Props } from '@theme/PaginatorNavLink';
9import PaginatorNavLink from '@theme-original/PaginatorNavLink';
10
11export default function PaginatorNavLinkWrapper(props: Props) {
12 const modifiedProps = { ...props };
13 if (modifiedProps.permalink === '/') {
14 modifiedProps.permalink = '/#blog';
15 }
16 return <PaginatorNavLink {...modifiedProps} />;
17}
diff --git a/src/theme/Root.tsx b/src/theme/Root.tsx
index 9459e9f..32462bb 100644
--- a/src/theme/Root.tsx
+++ b/src/theme/Root.tsx
@@ -4,14 +4,25 @@
4 * SPDX-License-Identifier: MIT 4 * SPDX-License-Identifier: MIT
5 */ 5 */
6 6
7import Head from '@docusaurus/Head';
7import type { Props } from '@theme/Root'; 8import type { Props } from '@theme/Root';
8import Root from '@theme-original/Root'; 9import Root from '@theme-original/Root';
9 10
10import ActiveSectionProvider from '@site/src/components/ActiveSectionProvider'; 11import ActiveSectionProvider from '@site/src/components/ActiveSectionProvider';
12import fontURL from '@site/src/fonts/recursive-latin.woff2?url';
11 13
12export default function RootWrapper(props: Props) { 14export default function RootWrapper(props: Props) {
13 return ( 15 return (
14 <ActiveSectionProvider> 16 <ActiveSectionProvider>
17 <Head>
18 <link
19 rel="preload"
20 href={fontURL}
21 as="font"
22 type="font/woff2"
23 crossOrigin="anonymous"
24 />
25 </Head>
15 <Root {...props} /> 26 <Root {...props} />
16 </ActiveSectionProvider> 27 </ActiveSectionProvider>
17 ); 28 );
diff --git a/src/theme/SiteMetadata.jsx b/src/theme/SiteMetadata.jsx
deleted file mode 100644
index 1639677..0000000
--- a/src/theme/SiteMetadata.jsx
+++ /dev/null
@@ -1,28 +0,0 @@
1/*
2 * SPDX-FileCopyrightText: 2024 Kristóf Marussy
3 *
4 * SPDX-License-Identifier: MIT
5 */
6
7import Head from '@docusaurus/Head';
8
9import SiteMetadata from '@theme-original/SiteMetadata';
10
11import fontURL from '@site/src/fonts/recursive-latin.woff2?url';
12
13export default function SiteMetadataWrapper(props) {
14 return (
15 <>
16 <Head>
17 <link
18 rel="preload"
19 href={fontURL}
20 as="font"
21 type="font/woff2"
22 crossOrigin="anonymous"
23 />
24 </Head>
25 <SiteMetadata {...props} />
26 </>
27 );
28}