1
mirror of https://github.com/jakejarvis/jarv.is.git synced 2025-06-30 22:26:38 -04:00

custom <Link /> wrapper around next/link

This commit is contained in:
2022-01-30 10:33:40 -05:00
parent 2a29d713bb
commit 9f34cec930
35 changed files with 578 additions and 1009 deletions

View File

@ -1,13 +1,12 @@
import classNames from "classnames";
import CopyButton from "../CopyButton/CopyButton";
import type { ReactNode } from "react";
import type { PropsWithChildren } from "react";
import styles from "./CodeBlock.module.css";
type Props = {
children: ReactNode;
type Props = PropsWithChildren<{
className?: string;
};
}>;
const CodeBlock = ({ children, className, ...rest }: Props) => {
if (className?.split(" ").includes("code-highlight")) {

View File

@ -1,15 +1,11 @@
import { memo } from "react";
import Link from "next/link";
import type { ReactNode } from "react";
import css from "styled-jsx/css";
import classNames from "classnames";
import Link, { Props as CustomLinkProps } from "../Link/Link";
type Props = {
children: ReactNode;
href: string;
type Props = CustomLinkProps & {
lightColor: string;
darkColor: string;
title?: string;
external?: boolean;
className?: string;
};
// spits out alpha'd version of given color in rgba() format within a linear-gradient (that's not really a gradient)
@ -25,28 +21,23 @@ const getLinearGradient = (hex: string, alpha = 0.4) => {
return `linear-gradient(${rgbaString},${rgbaString})`;
};
const ColorfulLink = ({ href, lightColor, darkColor, external, className, ...rest }: Props) => {
const ColorfulLink = ({ lightColor, darkColor, className, ...rest }: Props) => {
const { className: underlineClassName, styles: underlineStyles } = css.resolve`
a {
color: ${lightColor};
background-image: ${getLinearGradient(lightColor)};
}
:global([data-theme="dark"]) a {
color: ${darkColor};
background-image: ${getLinearGradient(darkColor)};
}
`;
return (
<>
<Link href={href} passHref={true} prefetch={false}>
<a
className={className}
target={external ? "_blank" : undefined}
rel={external ? "noopener noreferrer" : undefined}
{...rest}
/>
</Link>
<Link className={classNames(underlineClassName, className)} {...rest} />
<style jsx>{`
a {
color: ${lightColor};
background-image: ${getLinearGradient(lightColor)};
}
:global([data-theme="dark"]) a {
color: ${darkColor};
background-image: ${getLinearGradient(darkColor)};
}
`}</style>
{underlineStyles}
</>
);
};

View File

@ -33,10 +33,6 @@
line-height: 1.75;
}
.markdown_tip a:first-of-type {
font-weight: 500;
}
.hcaptcha {
margin: 1em 0;
}

View File

@ -3,6 +3,7 @@ import { useTheme } from "next-themes";
import classNames from "classnames/bind";
import { Formik, Form, Field } from "formik";
import HCaptcha from "@hcaptcha/react-hcaptcha";
import Link from "../Link/Link";
import { SendIcon, CheckOcticon, XOcticon } from "../Icons";
import type { FormikHelpers } from "formik";
@ -126,18 +127,13 @@ const ContactForm = ({ className }: Props) => {
<div className={styles.markdown_tip}>
Basic{" "}
<a
href="https://commonmark.org/help/"
title="Markdown reference sheet"
target="_blank"
rel="noopener noreferrer"
>
<Link href="https://commonmark.org/help/" title="Markdown reference sheet" style={{ fontWeight: 600 }}>
Markdown syntax
</a>{" "}
</Link>{" "}
is allowed here, e.g.: <strong>**bold**</strong>, <em>_italics_</em>, [
<a href="https://jarv.is" target="_blank" rel="noopener noreferrer">
<Link href="https://jarv.is" forceNewWindow>
links
</a>
</Link>
](https://jarv.is), and <code>`code`</code>.
</div>

View File

@ -3,28 +3,6 @@
line-height: 1.7;
}
.content a {
color: var(--link);
background-image: linear-gradient(var(--link-underline), var(--link-underline));
background-position: 0% 100%;
background-repeat: no-repeat;
background-size: 0% var(--link-underline-size);
padding-bottom: var(--link-underline-size);
/* background-size is for hover animation, color & border are for theme fading: */
transition: background-size 0.25s ease-in-out, color 0.25s ease, border 0.25s ease;
}
.content a:hover {
background-size: 100% var(--link-underline-size);
}
/* set an anchor's class to `no-underline` to disable all of the above */
.content :global(a.no-underline) {
background: none;
padding-bottom: 0;
transition: none;
}
@media screen and (max-width: 768px) {
.content {
font-size: 0.925em;

View File

@ -1,12 +1,11 @@
import classNames from "classnames";
import type { ReactNode } from "react";
import type { PropsWithChildren } from "react";
import styles from "./Content.module.css";
type Props = {
children: ReactNode;
type Props = PropsWithChildren<{
className?: string;
};
}>;
const Content = ({ className, ...rest }: Props) => <div className={classNames(styles.content, className)} {...rest} />;

View File

@ -1,16 +1,16 @@
import Image from "../Image/Image";
import innerText from "react-innertext";
import classNames from "classnames";
import type { ReactNode } from "react";
import type { PropsWithChildren } from "react";
import type { ImageProps as NextImageProps } from "next/image";
import styles from "./Figure.module.css";
type Props = Omit<NextImageProps, "alt"> & {
children: ReactNode;
alt?: string; // becomes optional -- pulled from plaintext-ified caption if missing
className?: string;
};
type Props = Omit<NextImageProps, "alt"> &
PropsWithChildren<{
alt?: string; // becomes optional -- pulled from plaintext-ified caption if missing
className?: string;
}>;
const Figure = ({ children, alt, className, ...imageProps }: Props) => {
return (

View File

@ -22,9 +22,10 @@
}
.icon {
width: 1.3em;
height: 1.3em;
vertical-align: -0.2em;
width: 1.25em;
height: 1.25em;
vertical-align: -0.25em;
margin: 0 0.075em;
}
.nextjs:hover {

View File

@ -16,7 +16,7 @@ const Heading = ({ as: Component, id, className, children, ...rest }: Props) =>
{/* add anchor link to H2s and H3s. ID is already generated by rehype-slug. `#` character inserted via CSS. */}
{id && (Component === "h2" || Component === "h3") && (
<a className={classNames("no-underline", styles.anchor)} href={`#${id}`} tabIndex={-1} aria-hidden="true" />
<a className={styles.anchor} href={`#${id}`} tabIndex={-1} aria-hidden="true" />
)}
</Component>
);

View File

@ -1,17 +1,18 @@
import Head from "next/head";
import { useTheme } from "next-themes";
import classNames from "classnames";
import Header from "../Header/Header";
import Footer from "../Footer/Footer";
import { themeColors } from "../../lib/config";
import type { ReactNode } from "react";
import type { PropsWithChildren } from "react";
import styles from "./Layout.module.css";
type Props = {
children: ReactNode;
};
type Props = PropsWithChildren<{
className?: string;
}>;
const Layout = ({ children }: Props) => {
const Layout = ({ className, children }: Props) => {
const { resolvedTheme } = useTheme();
return (
@ -23,7 +24,7 @@ const Layout = ({ children }: Props) => {
)}
<Header />
<main className={styles.main}>
<main className={classNames(styles.main, className)}>
<div className={styles.container}>{children}</div>
</main>
<Footer />

View File

@ -0,0 +1,14 @@
.link {
color: var(--link);
background-image: linear-gradient(var(--link-underline), var(--link-underline));
background-position: 0% 100%;
background-repeat: no-repeat;
background-size: 0% var(--link-underline-size);
padding-bottom: var(--link-underline-size);
/* background-size is for hover animation, color & border are for theme fading: */
transition: background-size 0.25s ease-in-out, color 0.25s ease, border 0.25s ease;
}
.link:hover {
background-size: 100% var(--link-underline-size);
}

49
components/Link/Link.tsx Normal file
View File

@ -0,0 +1,49 @@
import NextLink from "next/link";
import classNames from "classnames";
import isAbsoluteUrl from "is-absolute-url";
import type { AnchorHTMLAttributes, PropsWithChildren } from "react";
import type { LinkProps } from "next/link";
import styles from "./Link.module.css";
export type Props = Omit<AnchorHTMLAttributes<HTMLAnchorElement>, "href"> &
LinkProps &
PropsWithChildren<{
target?: string;
rel?: string;
forceNewWindow?: boolean;
className?: string;
}>;
const CustomLink = ({
href,
prefetch = false,
passHref = true,
target,
rel,
forceNewWindow,
className,
...rest
}: Props) => {
// this component auto-detects whether or not we should use a normal HTML anchor externally or next/link internally,
// can be overridden with `forceNewWindow={true}`.
if (forceNewWindow || isAbsoluteUrl(href.toString())) {
return (
<a
href={href.toString()}
target={target || "_blank"}
rel={rel || "noopener noreferrer"}
className={classNames(styles.link, className)}
{...rest}
/>
);
} else {
return (
<NextLink href={href} prefetch={prefetch} passHref={passHref}>
<a className={classNames(styles.link, className)} {...rest} />
</NextLink>
);
}
};
export default CustomLink;

View File

@ -1,12 +1,11 @@
import classNames from "classnames";
import type { ReactNode } from "react";
import type { PropsWithChildren } from "react";
import styles from "./List.module.css";
type Props = {
children: ReactNode;
type Props = PropsWithChildren<{
className?: string;
};
}>;
export const UnorderedList = ({ className, ...rest }: Props) => (
<ul className={classNames(styles.unordered, className)} {...rest} />

View File

@ -1,4 +1,3 @@
import Link from "next/link";
import { format } from "date-fns";
import HitCounter from "../HitCounter/HitCounter";
import { DateIcon, TagIcon, EditIcon, ViewsIcon } from "../Icons";
@ -15,11 +14,7 @@ const NoteMeta = ({ slug, date, title, tags = [] }: Props) => (
<span>
<DateIcon className={styles.icon} />
</span>
<span title={format(new Date(date), "PPppp")}>
<Link href={`/notes/${slug}/`}>
<a>{format(new Date(date), "MMMM d, yyyy")}</a>
</Link>
</span>
<span title={format(new Date(date), "PPppp")}>{format(new Date(date), "MMMM d, yyyy")}</span>
</div>
{tags.length > 0 && (
@ -36,19 +31,17 @@ const NoteMeta = ({ slug, date, title, tags = [] }: Props) => (
)}
<div>
<span>
<EditIcon className={styles.icon} />
</span>
<span>
<a
href={`https://github.com/${config.githubRepo}/blob/main/notes/${slug}.mdx`}
target="_blank"
rel="noopener noreferrer"
title={`Edit "${title}" on GitHub`}
>
Improve This Post
</a>
</span>
<a
href={`https://github.com/${config.githubRepo}/blob/main/notes/${slug}.mdx`}
target="_blank"
rel="noopener noreferrer"
title={`Edit "${title}" on GitHub`}
>
<span>
<EditIcon className={styles.icon} />
</span>
<span>Improve This Post</span>
</a>
</div>
<div className={styles.views}>

View File

@ -5,10 +5,15 @@ import styles from "./NoteTitle.module.css";
type Props = Pick<NoteMetaType, "slug" | "htmlTitle">;
const NoteTitle = ({ slug, htmlTitle }: Props) => (
const NoteTitle = ({ slug, htmlTitle, ...rest }: Props) => (
<h1 className={styles.title}>
<Link href={`/notes/${slug}/`}>
<a dangerouslySetInnerHTML={{ __html: htmlTitle }} />
<Link
href={{
pathname: "/notes/[slug]/",
query: { slug: slug },
}}
>
<a dangerouslySetInnerHTML={{ __html: htmlTitle }} {...rest} />
</Link>
</h1>
);

View File

@ -1,5 +1,5 @@
import Link from "next/link";
import { format } from "date-fns";
import Link from "../Link/Link";
import type { NoteMetaType } from "../../types";
import styles from "./NotesList.module.css";
@ -21,10 +21,8 @@ const NotesList = ({ notesByYear }) => {
pathname: "/notes/[slug]/",
query: { slug: slug },
}}
prefetch={false}
>
<a dangerouslySetInnerHTML={{ __html: htmlTitle }} />
</Link>
dangerouslySetInnerHTML={{ __html: htmlTitle }}
/>
</span>
</li>
))}

View File

@ -8,13 +8,8 @@ type Props = {
className?: string;
};
const OctocatLink = ({ repo, className }: Props) => (
<a
className={classNames("no-underline", styles.link)}
href={`https://github.com/${repo}`}
target="_blank"
rel="noopener noreferrer"
>
const OctocatLink = ({ repo, className, ...rest }: Props) => (
<a className={styles.link} href={`https://github.com/${repo}`} target="_blank" rel="noopener noreferrer" {...rest}>
<OctocatOcticon fill="currentColor" className={classNames(styles.icon, className)} />
</a>
);

View File

@ -2,23 +2,22 @@ import { useRouter } from "next/router";
import Link from "next/link";
import classNames from "classnames";
import { baseUrl } from "../../lib/config";
import type { ReactNode } from "react";
import type { PropsWithChildren } from "react";
import styles from "./PageTitle.module.css";
type Props = {
children: ReactNode;
type Props = PropsWithChildren<{
className?: string;
};
}>;
const PageTitle = ({ children, className }: Props) => {
const PageTitle = ({ className, ...rest }: Props) => {
const router = useRouter();
const canonical = `${baseUrl}${router.pathname}/`;
return (
<h1 className={classNames(styles.title, className)}>
<Link href={canonical}>
<a>{children}</a>
<a {...rest} />
</Link>
</h1>
);

View File

@ -1,5 +1,6 @@
import classNames from "classnames";
import { intlFormat, formatDistanceToNowStrict } from "date-fns";
import Link from "../Link/Link";
import { StarOcticon, ForkOcticon } from "../Icons";
import type { RepoType } from "../../types";
@ -11,9 +12,9 @@ type Props = RepoType & {
const RepositoryCard = ({ name, url, description, language, stars, forks, updatedAt, className }: Props) => (
<div className={classNames(styles.card, className)}>
<a className={styles.name} href={url} target="_blank" rel="noopener noreferrer">
<Link className={styles.name} href={url}>
{name}
</a>
</Link>
{description && <p className={styles.description}>{description}</p>}
@ -28,7 +29,6 @@ const RepositoryCard = ({ name, url, description, language, stars, forks, update
{stars > 0 && (
<div className={styles.meta_item}>
<a
className="no-underline"
href={`${url}/stargazers`}
title={`${stars.toLocaleString("en-US")} ${stars === 1 ? "star" : "stars"}`}
target="_blank"
@ -43,7 +43,6 @@ const RepositoryCard = ({ name, url, description, language, stars, forks, update
{forks > 0 && (
<div className={styles.meta_item}>
<a
className="no-underline"
href={`${url}/network/members`}
title={`${forks.toLocaleString("en-US")} ${forks === 1 ? "fork" : "forks"}`}
target="_blank"

View File

@ -1,16 +1,15 @@
import { useEffect, useRef } from "react";
import classNames from "classnames/bind";
import type { ReactNode } from "react";
import type { PropsWithChildren } from "react";
import styles from "./Wallpaper.module.css";
const cx = classNames.bind(styles);
type Props = {
type Props = PropsWithChildren<{
image: string;
tile?: boolean;
className?: string;
children: ReactNode;
};
}>;
const Wallpaper = ({ image, tile, className, ...rest }: Props) => {
const bgRef = useRef<HTMLDivElement>(null);

View File

@ -5,6 +5,7 @@ export { default as Image } from "../components/Image/Image";
export { default as Figure } from "../components/Figure/Figure";
// These (mostly very small) components are direct replacements for HTML tags generated by remark:
export { default as a } from "../components/Link/Link";
export { default as code } from "../components/CodeBlock/CodeBlock";
export { default as blockquote } from "../components/Blockquote/Blockquote";
export { default as hr } from "../components/HorizontalRule/HorizontalRule";

View File

@ -12,7 +12,6 @@ import { NOTES_DIR, baseUrl } from "./config";
// remark/rehype markdown plugins
import remarkGfm from "remark-gfm";
import rehypeExternalLinks from "rehype-external-links";
import rehypeSlug from "rehype-slug";
// note: 'common' only exports these languages: https://github.com/wooorm/refractor/blob/main/lib/common.js
// eslint-disable-next-line import/no-unresolved
@ -70,11 +69,7 @@ export const getNote = async (slug: string): Promise<NoteType> => {
parseFrontmatter: false,
mdxOptions: {
remarkPlugins: [[remarkGfm, { singleTilde: false }]],
rehypePlugins: [
[rehypeExternalLinks, { target: "_blank", rel: ["noopener", "noreferrer"] }],
[rehypeSlug, {}],
[rehypePrism, { ignoreMissing: true }],
],
rehypePlugins: [[rehypeSlug], [rehypePrism, { ignoreMissing: true }]],
},
});

View File

@ -43,6 +43,7 @@
"feed": "^4.2.2",
"formik": "^2.2.9",
"gray-matter": "^4.0.3",
"is-absolute-url": "^4.0.1",
"markdown-to-jsx": "^7.1.6",
"modern-normalize": "^1.1.0",
"next": "^12.0.9",
@ -63,20 +64,19 @@
"react-player": "^2.9.0",
"react-tweet-embed": "^1.3.1",
"reading-time": "^1.5.0",
"rehype-external-links": "^1.0.1",
"rehype-prism-plus": "^1.3.1",
"rehype-slug": "^5.0.1",
"remark-gfm": "^3.0.1",
"remove-markdown": "^0.3.0",
"sanitize-html": "^2.6.1",
"simple-icons": "^6.7.0",
"simple-icons": "^6.8.0",
"swr": "^1.2.0",
"terser": "^5.10.0",
"twemoji": "github:twitter/twemoji#v13.1.0"
},
"devDependencies": {
"@jakejarvis/eslint-config": "github:jakejarvis/eslint-config#main",
"@svgr/webpack": "^6.2.0",
"@svgr/webpack": "^6.2.1",
"@types/node": "*",
"@types/prop-types": "^15.7.4",
"@types/react": "^17.0.38",

View File

@ -1,6 +1,7 @@
import { NextSeo } from "next-seo";
import Content from "../components/Content/Content";
import PageTitle from "../components/PageTitle/PageTitle";
import Link from "../components/Link/Link";
import Image from "../components/Image/Image";
import Blockquote from "../components/Blockquote/Blockquote";
import CodeBlock from "../components/CodeBlock/CodeBlock";
@ -23,20 +24,12 @@ const CLI = () => (
<Content>
<Blockquote>
<p>
The{" "}
<a href="https://jarv.is/" target="_blank" rel="noopener noreferrer">
Jake Jarvis
</a>{" "}
CLI (aka the most useless Node module ever published, in history, by anyone, ever).
The <Link href="https://jarv.is/">Jake Jarvis</Link> CLI (aka the most useless Node module ever published, in
history, by anyone, ever).
</p>
</Blockquote>
<a
className="no-underline"
href="https://www.npmjs.com/package/@jakejarvis/cli"
target="_blank"
rel="noopener noreferrer"
>
<a href="https://www.npmjs.com/package/@jakejarvis/cli" target="_blank" rel="noopener noreferrer">
<Image src={cliImg} alt="Terminal Screenshot" priority />
</a>
@ -46,48 +39,32 @@ const CLI = () => (
<H2>Inspired by</H2>
<ul>
<li>
<a href="https://github.com/sindresorhus/sindresorhus-cli" target="_blank" rel="noopener noreferrer">
@sindresorhus/sindresorhus-cli
</a>
<Link href="https://github.com/sindresorhus/sindresorhus-cli">@sindresorhus/sindresorhus-cli</Link>
</li>
<li>
<a href="https://github.com/yg/ygcodes" target="_blank" rel="noopener noreferrer">
@yg/ygcodes
</a>
<Link href="https://github.com/yg/ygcodes">@yg/ygcodes</Link>
</li>
</ul>
<H2>Built with</H2>
<ul>
<li>
<a href="https://github.com/vadimdemedes/ink" target="_blank" rel="noopener noreferrer">
ink
</a>{" "}
- React for interactive command-line apps
<Link href="https://github.com/vadimdemedes/ink">ink</Link> - React for interactive command-line apps
</li>
<li>
<a href="https://github.com/sindresorhus/meow" target="_blank" rel="noopener noreferrer">
meow
</a>{" "}
- CLI helper
<Link href="https://github.com/sindresorhus/meow">meow</Link> - CLI helper
</li>
</ul>
<p>
<a href="https://github.com/jakejarvis/jakejarvis/tree/main/cli" target="_blank" rel="noreferrer">
<Link href="https://github.com/jakejarvis/jakejarvis/tree/main/cli" target="_blank" rel="noreferrer">
View source on GitHub.
</a>
</Link>
</p>
<H2>License</H2>
<p>
MIT ©{" "}
<a href="https://jarv.is/" target="_blank" rel="noopener noreferrer">
Jake Jarvis
</a>
,{" "}
<a href="https://sindresorhus.com" target="_blank" rel="noopener noreferrer">
Sindre Sorhus
</a>
MIT © <Link href="https://jarv.is/">Jake Jarvis</Link>,{" "}
<Link href="https://sindresorhus.com">Sindre Sorhus</Link>
</p>
</Content>
</>

View File

@ -1,6 +1,7 @@
import { NextSeo } from "next-seo";
import Content from "../components/Content/Content";
import PageTitle from "../components/PageTitle/PageTitle";
import Link from "../components/Link/Link";
import ContactForm from "../components/ContactForm/ContactForm";
const Contact = () => (
@ -18,21 +19,15 @@ const Contact = () => (
<div className="wrapper">
<p>
Fill out this quick form and I'll get back to you as soon as I can! You can also{" "}
<a href="mailto:jake@jarv.is">email me directly</a>, send me a{" "}
<a
href="https://twitter.com/messages/compose?recipient_id=229769022"
target="_blank"
rel="noopener nofollow noreferrer"
>
direct message on Twitter
</a>
, or <a href="sms:+1-617-917-3737">text me</a>.
<Link href="mailto:jake@jarv.is">email me directly</Link>, send me a{" "}
<Link href="https://twitter.com/messages/compose?recipient_id=229769022">direct message on Twitter</Link>, or{" "}
<Link href="sms:+1-617-917-3737">text me</Link>.
</p>
<p>
🔐 You can grab my public key here:{" "}
<a href="/pubkey.asc" title="My Public PGP Key" target="_blank" rel="pgpkey authn noopener">
<Link href="/pubkey.asc" title="My Public PGP Key" rel="pgpkey authn noopener" forceNewWindow>
<code className="pubkey">6BF3 79D3 6F67 1480 2B0C 9CF2 51E6 9A39</code>
</a>
</Link>
.
</p>

View File

@ -1,6 +1,7 @@
import { NextSeo } from "next-seo";
import Content from "../components/Content/Content";
import PageTitle from "../components/PageTitle/PageTitle";
import Link from "../components/Link/Link";
import Video from "../components/Video/Video";
import thumbnail from "../public/static/images/hillary/thumb.png";
@ -26,17 +27,17 @@ const Hillary = () => (
<p className="copyright">
Video is property of{" "}
<a href="https://www.hillaryclinton.com/" target="_blank" rel="noopener noreferrer">
<Link href="https://www.hillaryclinton.com/" style={{ fontWeight: 700 }}>
Hillary for America
</a>
</Link>
, the{" "}
<a href="https://democrats.org/" target="_blank" rel="noopener noreferrer">
<Link href="https://democrats.org/" style={{ fontWeight: 700 }}>
Democratic National Committee
</a>
</Link>
, and{" "}
<a href="https://cnnpressroom.blogs.cnn.com/" target="_blank" rel="noopener noreferrer">
<Link href="https://cnnpressroom.blogs.cnn.com/" style={{ fontWeight: 700 }}>
CNN / WarnerMedia
</a>
</Link>
. &copy; 2016.
</p>
</Content>
@ -49,10 +50,6 @@ const Hillary = () => (
margin: 1.25em 1em 0.5em;
color: var(--medium-light);
}
.copyright a {
font-weight: 700;
}
`}</style>
</>
);

View File

@ -4,7 +4,7 @@ import ColorfulLink from "../components/ColorfulLink/ColorfulLink";
const Index = () => (
<>
<Content>
<div className="home">
<div>
<h1>
Hi there! I'm Jake. <span className="wave">👋</span>
</h1>
@ -16,7 +16,6 @@ const Index = () => (
title='"Boston Accent Trailer - Late Night with Seth Meyers" on YouTube'
lightColor="#fb4d42"
darkColor="#ff5146"
external
>
Boston
</ColorfulLink>
@ -30,7 +29,6 @@ const Index = () => (
title='"The Brutal Lifecycle of JavaScript Frameworks" by Ian Allen'
lightColor="#1091b3"
darkColor="#6fcbe3"
external
>
modern JS frameworks
</ColorfulLink>{" "}
@ -40,18 +38,11 @@ const Index = () => (
title="The best JS framework in the world by Eric Wastl"
lightColor="#f48024"
darkColor="#e18431"
external
>
vanilla JavaScript
</ColorfulLink>{" "}
to make nifty{" "}
<ColorfulLink
href="https://jamstack.wtf/"
title="WTF is JAMstack?"
lightColor="#04a699"
darkColor="#08bbac"
external
>
<ColorfulLink href="https://jamstack.wtf/" title="WTF is JAMstack?" lightColor="#04a699" darkColor="#08bbac">
JAMstack sites
</ColorfulLink>{" "}
with dynamic{" "}
@ -60,7 +51,6 @@ const Index = () => (
title="Node.js Official Website"
lightColor="#6fbc4e"
darkColor="#84d95f"
external
>
Node.js
</ColorfulLink>{" "}
@ -70,7 +60,6 @@ const Index = () => (
title='"PHP in 2020" by Brent Roose'
lightColor="#8892bf"
darkColor="#a4afe3"
external
>
PHP
</ColorfulLink>
@ -80,7 +69,6 @@ const Index = () => (
title="Ruby Official Website"
lightColor="#d34135"
darkColor="#f95a4d"
external
>
Ruby
</ColorfulLink>
@ -90,7 +78,6 @@ const Index = () => (
title="Golang Official Website"
lightColor="#00acd7"
darkColor="#2ad1fb"
external
>
Go
</ColorfulLink>{" "}
@ -104,7 +91,6 @@ const Index = () => (
title="jakejarvis/awesome-shodan-queries on GitHub"
lightColor="#00b81a"
darkColor="#57f06d"
external
>
application security
</ColorfulLink>
@ -114,7 +100,6 @@ const Index = () => (
title='"What is serverless computing?" on Cloudflare'
lightColor="#0098ec"
darkColor="#43b9fb"
external
>
serverless stacks
</ColorfulLink>
@ -124,7 +109,6 @@ const Index = () => (
title='"Automation" on xkcd'
lightColor="#ff6200"
darkColor="#f46c16"
external
>
DevOps automation
</ColorfulLink>
@ -171,7 +155,6 @@ const Index = () => (
title='"Student designs iPhone JoeyTracker app" on The Tufts Daily'
lightColor="#ff1b1b"
darkColor="#f06060"
external
>
have
</ColorfulLink>{" "}
@ -188,7 +171,6 @@ const Index = () => (
title='"The Facebook Effect" by David Kirkpatrick (Google Books)'
lightColor="#f2b702"
darkColor="#ffcc2e"
external
>
featured
</ColorfulLink>{" "}
@ -197,7 +179,6 @@ const Index = () => (
title='"The new Facebook is on a roll" on CNN Money'
lightColor="#5ebd3e"
darkColor="#78df55"
external
>
by
</ColorfulLink>{" "}
@ -206,7 +187,6 @@ const Index = () => (
title='"Middio: A YouTube Scraper for Major Label Music Videos" on Wired'
lightColor="#009cdf"
darkColor="#29bfff"
external
>
various
</ColorfulLink>{" "}
@ -215,7 +195,6 @@ const Index = () => (
title='"Fresh Faces in Tech: 10 Kid Entrepreneurs to Watch" on Gigaom'
lightColor="#3e49bb"
darkColor="#7b87ff"
external
>
media
</ColorfulLink>{" "}
@ -224,7 +203,6 @@ const Index = () => (
title='"Your Next Client? The CEO&#39;s Son" on Advertising Age'
lightColor="#973999"
darkColor="#db60dd"
external
>
outlets
</ColorfulLink>
@ -238,7 +216,6 @@ const Index = () => (
title="Jake Jarvis on GitHub"
lightColor="#8d4eff"
darkColor="#a379f0"
external
>
GitHub
</ColorfulLink>{" "}
@ -248,7 +225,6 @@ const Index = () => (
title="Jake Jarvis on LinkedIn"
lightColor="#0073b1"
darkColor="#3b9dd2"
external
>
LinkedIn
</ColorfulLink>
@ -258,12 +234,13 @@ const Index = () => (
</ColorfulLink>{" "}
<sup className="monospace pgp_key">
<ColorfulLink
className="no-underline"
href="/pubkey.asc"
rel="pgpkey authn noopener"
title="My Public Key"
lightColor="#757575"
darkColor="#959595"
external
style={{ background: "none" }}
forceNewWindow
>
🔐 2B0C 9CF2 51E6 9A39
</ColorfulLink>
@ -274,7 +251,6 @@ const Index = () => (
title="Jake Jarvis on Twitter"
lightColor="#00acee"
darkColor="#3bc9ff"
external
>
Twitter
</ColorfulLink>
@ -293,32 +269,32 @@ const Index = () => (
</Content>
<style jsx>{`
.home {
div {
font-size: 1.1em;
line-height: 1;
}
.home h1 {
h1 {
margin: 0 0 0.5em -0.03em;
font-size: 1.8em;
font-weight: 500;
letter-spacing: -0.01em;
}
.home h2 {
h2 {
margin: 0.5em 0 0.5em -0.03em;
font-size: 1.35em;
font-weight: 400;
letter-spacing: -0.016em;
line-height: 1.4;
}
.home p {
p {
margin: 0.85em 0;
letter-spacing: -0.004em;
line-height: 1.7;
}
.home p:last-of-type {
p:last-of-type {
margin-bottom: 0;
}
.home .wave {
.wave {
display: inline-block;
margin-left: 0.1em;
font-size: 1.2em;
@ -327,18 +303,15 @@ const Index = () => (
transform-origin: 65% 80%;
will-change: transform;
}
.home .pgp_key {
.pgp_key {
margin: 0 0.15em;
font-size: 0.65em;
word-spacing: -0.3em;
}
.home .pgp_key :global(a) {
background: none !important;
}
.home .quiet {
.quiet {
color: var(--medium-light);
}
.home .birthday :global(a:hover) {
.birthday :global(a:hover) {
/* magic wand cursor easter egg */
cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='24' height='30' style='font-size:24px'><text y='50%' transform='rotate(-70 0 0) translate(-20, 6)'>🪄</text></svg>")
5 5,
@ -346,17 +319,18 @@ const Index = () => (
}
@media screen and (max-width: 768px) {
.home h1 {
h1 {
font-size: 1.5em;
}
.home h2 {
h2 {
font-size: 1.2em;
}
.home p {
p {
font-size: 0.925em;
}
}
/* https://jarv.is/notes/css-waving-hand-emoji/ */
@keyframes wave {
0% {
transform: rotate(0deg);

View File

@ -1,6 +1,7 @@
import { NextSeo } from "next-seo";
import Content from "../components/Content/Content";
import PageTitle from "../components/PageTitle/PageTitle";
import Link from "../components/Link/Link";
import Video from "../components/Video/Video";
import thumbnail from "../public/static/images/leo/thumb.png";
@ -27,17 +28,13 @@ const Leo = () => (
<p className="copyright">
Video is property of{" "}
<a
href="https://web.archive.org/web/20070511004304/http://www.g4techtv.ca/"
target="_blank"
rel="noopener noreferrer"
>
<Link href="https://web.archive.org/web/20070511004304/http://www.g4techtv.ca/" style={{ fontWeight: 700 }}>
G4techTV Canada
</a>{" "}
</Link>{" "}
&amp;{" "}
<a href="https://leolaporte.com/" target="_blank" rel="noopener noreferrer">
<Link href="https://leolaporte.com/" style={{ fontWeight: 700 }}>
Leo Laporte
</a>
</Link>
. &copy; 2007 G4 Media, Inc.
</p>
</Content>
@ -50,10 +47,6 @@ const Leo = () => (
margin: 1.25em 1em 0.5em;
color: var(--medium-light);
}
.copyright a {
font-weight: 700;
}
`}</style>
</>
);

View File

@ -1,6 +1,7 @@
import { NextSeo } from "next-seo";
import Content from "../components/Content/Content";
import PageTitle from "../components/PageTitle/PageTitle";
import Link from "../components/Link/Link";
import HorizontalRule from "../components/HorizontalRule/HorizontalRule";
import Blockquote from "../components/Blockquote/Blockquote";
import { H2, H3 } from "../components/Heading/Heading";
@ -19,18 +20,14 @@ const License = () => (
<Content>
<p>
Unless otherwise noted, content on this website is published under the{" "}
<a href="https://creativecommons.org/licenses/by/4.0/" target="_blank" rel="noopener noreferrer">
<Link href="https://creativecommons.org/licenses/by/4.0/">
<strong>Creative Commons Attribution 4.0 International Public License</strong>
</a>{" "}
</Link>{" "}
(CC-BY-4.0), which means that you can copy, redistribute, remix, transform, and build upon the content for any
purpose as long as you give appropriate credit (such as a hyperlink to the original URL).
</p>
<p>
The{" "}
<a href="https://creativecommons.org/licenses/by/4.0/legalcode" target="_blank" rel="noopener noreferrer">
full license
</a>{" "}
is re-printed below.
The <Link href="https://creativecommons.org/licenses/by/4.0/legalcode">full license</Link> is re-printed below.
</p>
<HorizontalRule />
@ -39,7 +36,6 @@ const License = () => (
<p style={{ textAlign: "center", lineHeight: 0 }}>
<a
className="no-underline"
href="https://creativecommons.org/licenses/by/4.0/"
target="_blank"
rel="noopener noreferrer"
@ -98,13 +94,9 @@ const License = () => (
our licenses so that the public can reuse the material as expected. Licensors should clearly mark any
material not subject to the license. This includes other CC-licensed material, or material used under an
exception or limitation to copyright.{" "}
<a
href="https://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensors"
target="_blank"
rel="noopener noreferrer"
>
<Link href="https://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensors">
More considerations for licensors
</a>
</Link>
.
</p>
</li>
@ -118,13 +110,9 @@ const License = () => (
restricted for other reasons, including because others have copyright or other rights in the material. A
licensor may make special requests, such as asking that all changes be marked or described. Although not
required by our licenses, you are encouraged to respect those requests where reasonable.{" "}
<a
href="https://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensees"
target="_blank"
rel="noopener noreferrer"
>
<Link href="https://wiki.creativecommons.org/Considerations_for_licensors_and_licensees#Considerations_for_licensees">
More considerations for the public
</a>
</Link>
.
</p>
</li>
@ -461,30 +449,20 @@ const License = () => (
Creative Commons is not a party to its public licenses. Notwithstanding, Creative Commons may elect to apply
one of its public licenses to material it publishes and in those instances will be considered the "Licensor."
The text of the Creative Commons public licenses is dedicated to the public domain under the{" "}
<a
href="https://creativecommons.org/publicdomain/zero/1.0/legalcode"
target="_blank"
rel="noopener noreferrer"
>
<Link href="https://creativecommons.org/publicdomain/zero/1.0/legalcode">
<em>CC0 Public Domain Dedication</em>
</a>
</Link>
. Except for the limited purpose of indicating that material is shared under a Creative Commons public license
or as otherwise permitted by the Creative Commons policies published at{" "}
<a href="https://creativecommons.org/policies" target="_blank" rel="noopener noreferrer">
creativecommons.org/policies
</a>
, Creative Commons does not authorize the use of the trademark "Creative Commons" or any other trademark or
logo of Creative Commons without its prior written consent including, without limitation, in connection with
any unauthorized modifications to any of its public licenses or any other arrangements, understandings, or
agreements concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part
of the public licenses.
<Link href="https://creativecommons.org/policies">creativecommons.org/policies</Link>, Creative Commons does
not authorize the use of the trademark "Creative Commons" or any other trademark or logo of Creative Commons
without its prior written consent including, without limitation, in connection with any unauthorized
modifications to any of its public licenses or any other arrangements, understandings, or agreements
concerning use of licensed material. For the avoidance of doubt, this paragraph does not form part of the
public licenses.
</p>
<p>
Creative Commons may be contacted at{" "}
<a href="https://creativecommons.org/" target="_blank" rel="noopener noreferrer">
creativecommons.org
</a>
.
Creative Commons may be contacted at <Link href="https://creativecommons.org/">creativecommons.org</Link>.
</p>
</Blockquote>
</Content>

View File

@ -56,6 +56,7 @@ const Note = ({ frontMatter, source }: NoteType) => {
<NoteTitle slug={frontMatter.slug} htmlTitle={frontMatter.htmlTitle} />
<Content>
{/* @ts-ignore */}
<MDXRemote {...source} components={{ ...mdxComponents }} />
</Content>

View File

@ -1,7 +1,7 @@
import Link from "next/link";
import { NextSeo } from "next-seo";
import Content from "../components/Content/Content";
import PageTitle from "../components/PageTitle/PageTitle";
import Link from "../components/Link/Link";
import Figure from "../components/Figure/Figure";
import IFrame from "../components/IFrame/IFrame";
import HorizontalRule from "../components/HorizontalRule/HorizontalRule";
@ -40,9 +40,13 @@ const Previously = () => (
<Content>
<Figure src={img_wayback} alt="Timeline of this website's past." priority>
...the{" "}
<a href="https://web.archive.org/web/20010501000000*/jakejarvis.com" target="_blank" rel="noopener noreferrer">
<Link
href="https://web.archive.org/web/20010501000000*/jakejarvis.com"
target="_blank"
rel="noopener noreferrer"
>
Cringey Chronicles&trade;
</a>{" "}
</Link>{" "}
of this website's past.
</Figure>
@ -55,13 +59,11 @@ const Previously = () => (
</code>
...{" "}
<Link href="/y2k/" prefetch={false}>
<a>
Click for the{" "}
<strong>
<em>FULL</em>
</strong>{" "}
experience anyway.
</a>
Click for the{" "}
<strong>
<em>FULL</em>
</strong>{" "}
experience anyway.
</Link>
</p>
@ -73,9 +75,9 @@ const Previously = () => (
/>
<p className="iframe_caption">
November 2001 (
<a href="https://github.com/jakejarvis/my-first-website" target="_blank" rel="noopener noreferrer">
<Link href="https://github.com/jakejarvis/my-first-website" target="_blank" rel="noopener noreferrer">
archived source
</a>
</Link>
)
</p>
@ -123,9 +125,9 @@ const Previously = () => (
<Figure src={img_2012_09} alt="September 2012">
September 2012 (
<a href="https://github.com/jakejarvis/jarv.is/tree/v1" target="_blank" rel="noopener noreferrer">
<Link href="https://github.com/jakejarvis/jarv.is/tree/v1" target="_blank" rel="noopener noreferrer">
archived source
</a>
</Link>
)
</Figure>
@ -133,9 +135,9 @@ const Previously = () => (
<Figure src={img_2018_04} alt="April 2018">
April 2018 (
<a href="https://github.com/jakejarvis/jarv.is/tree/v2" target="_blank" rel="noopener noreferrer">
<Link href="https://github.com/jakejarvis/jarv.is/tree/v2" target="_blank" rel="noopener noreferrer">
archived source
</a>
</Link>
)
</Figure>
@ -143,9 +145,9 @@ const Previously = () => (
<Figure src={img_2020_03} alt="March 2020">
March 2020 (
<a href="https://github.com/jakejarvis/jarv.is/tree/v3" target="_blank" rel="noopener noreferrer">
<Link href="https://github.com/jakejarvis/jarv.is/tree/v3" target="_blank" rel="noopener noreferrer">
archived source
</a>
</Link>
)
</Figure>
</Content>

View File

@ -1,7 +1,7 @@
import Link from "next/link";
import { NextSeo } from "next-seo";
import Content from "../components/Content/Content";
import PageTitle from "../components/PageTitle/PageTitle";
import Link from "../components/Link/Link";
import Image from "../components/Image/Image";
import IFrame from "../components/IFrame/IFrame";
import { H2 } from "../components/Heading/Heading";
@ -27,33 +27,23 @@ const Privacy = () => (
<p>
Pages and first-party assets on this website are served by{" "}
<a href="https://vercel.com/" target="_blank" rel="noopener noreferrer">
<Link href="https://vercel.com/">
<strong> Vercel</strong>
</a>
. Refer to their{" "}
<a href="https://vercel.com/legal/privacy-policy" target="_blank" rel="noopener noreferrer">
privacy policy
</a>{" "}
for more information.
</Link>
. Refer to their <Link href="https://vercel.com/legal/privacy-policy">privacy policy</Link> for more
information.
</p>
<p>
For a likely excessive level of privacy and security, this website is also mirrored on the{" "}
<a href="https://www.torproject.org/" target="_blank" rel="noopener noreferrer">
🧅 Tor network
</a>{" "}
at:
<Link href="https://www.torproject.org/">🧅 Tor network</Link> at:
</p>
<Blockquote>
<p>
<a
href="http://jarvis2i2vp4j4tbxjogsnqdemnte5xhzyi7hziiyzxwge3hzmh57zad.onion"
target="_blank"
rel="noopener noreferrer"
>
<Link href="http://jarvis2i2vp4j4tbxjogsnqdemnte5xhzyi7hziiyzxwge3hzmh57zad.onion">
<strong>jarvis2i2vp4j4tbxjogsnqdemnte5xhzyi7hziiyzxwge3hzmh57zad.onion</strong>
</a>
</Link>
</p>
</Blockquote>
@ -61,53 +51,29 @@ const Privacy = () => (
<p>
A very simple hit counter on each blog post tallies an aggregate number of pageviews (i.e.{" "}
<code>hits = hits + 1</code>) in a{" "}
<a href="https://fauna.com/" target="_blank" rel="noopener noreferrer">
Fauna
</a>{" "}
database. Individual views and identifying (or non-identifying) details are{" "}
<strong>never stored or logged</strong>.
<code>hits = hits + 1</code>) in a <Link href="https://fauna.com/">Fauna</Link> database. Individual views and
identifying (or non-identifying) details are <strong>never stored or logged</strong>.
</p>
<p>
The{" "}
<a
href="https://github.com/jakejarvis/jarv.is/blob/main/pages/api/hits.ts"
target="_blank"
rel="noopener noreferrer"
>
serverless function
</a>{" "}
The <Link href="https://github.com/jakejarvis/jarv.is/blob/main/pages/api/hits.ts">serverless function</Link>{" "}
and{" "}
<a
href="https://github.com/jakejarvis/jarv.is/blob/main/components/HitCounter/HitCounter.tsx"
target="_blank"
rel="noopener noreferrer"
>
<Link href="https://github.com/jakejarvis/jarv.is/blob/main/components/HitCounter/HitCounter.tsx">
client script
</a>{" "}
are open source, and{" "}
<a href="https://github.com/jakejarvis/website-stats" target="_blank" rel="noopener noreferrer">
snapshots of the database
</a>{" "}
</Link>{" "}
are open source, and <Link href="https://github.com/jakejarvis/website-stats">snapshots of the database</Link>{" "}
are public.
</p>
<Image src={faunaImg} alt="The entire database schema." />
<p>
<a href="https://usefathom.com/ref/ZEYG0O" target="_blank" rel="noopener noreferrer">
<Link href="https://usefathom.com/ref/ZEYG0O">
<strong>Fathom Analytics</strong>
</a>
, a <em>very</em>{" "}
<a href="https://usefathom.com/privacy-focused-web-analytics" target="_blank" rel="noopener noreferrer">
privacy-focused
</a>{" "}
</Link>
, a <em>very</em> <Link href="https://usefathom.com/privacy-focused-web-analytics">privacy-focused</Link>{" "}
service, is also used to gain insights into referrers, search terms, etc.{" "}
<strong>without collecting anything identifiable about you</strong>. (My{" "}
<a href="/stats/" target="_blank" rel="noopener noreferrer">
dashboard
</a>{" "}
<strong>without collecting anything identifiable about you</strong>. (My <Link href="/stats/">dashboard</Link>{" "}
is completely public, too!)
</p>
@ -127,43 +93,25 @@ const Privacy = () => (
<ul>
<li>
<a href="https://blog.codepen.io/documentation/privacy/" target="_blank" rel="noopener noreferrer">
CodePen
</a>
<Link href="https://blog.codepen.io/documentation/privacy/">CodePen</Link>
</li>
<li>
<a href="https://www.facebook.com/policy.php" target="_blank" rel="noopener noreferrer">
Facebook
</a>
<Link href="https://www.facebook.com/policy.php">Facebook</Link>
</li>
<li>
<a
href="https://docs.github.com/en/github/site-policy/github-privacy-statement"
target="_blank"
rel="noopener noreferrer"
>
GitHub
</a>
<Link href="https://docs.github.com/en/github/site-policy/github-privacy-statement">GitHub</Link>
</li>
<li>
<a href="https://soundcloud.com/pages/privacy" target="_blank" rel="noopener noreferrer">
SoundCloud
</a>
<Link href="https://soundcloud.com/pages/privacy">SoundCloud</Link>
</li>
<li>
<a href="https://twitter.com/en/privacy" target="_blank" rel="noopener noreferrer">
Twitter
</a>
<Link href="https://twitter.com/en/privacy">Twitter</Link>
</li>
<li>
<a href="https://vimeo.com/privacy" target="_blank" rel="noopener noreferrer">
Vimeo
</a>
<Link href="https://vimeo.com/privacy">Vimeo</Link>
</li>
<li>
<a href="https://policies.google.com/privacy" target="_blank" rel="noopener noreferrer">
YouTube
</a>
<Link href="https://policies.google.com/privacy">YouTube</Link>
</li>
</ul>
@ -171,51 +119,27 @@ const Privacy = () => (
<p>
Using{" "}
<a href="https://www.hcaptcha.com/" target="_blank" rel="noopener noreferrer">
<Link href="https://www.hcaptcha.com/">
<strong>hCaptcha</strong>
</a>{" "}
to fight bot spam on the{" "}
<Link href="/contact/" prefetch={false}>
<a>contact form</a>
</Link>{" "}
was an easy choice over seemingly unavoidable alternatives like{" "}
<a href="https://developers.google.com/recaptcha/" target="_blank" rel="noopener noreferrer">
reCAPTCHA
</a>
.
to fight bot spam on the <Link href="/contact/">contact form</Link> was an easy choice over seemingly
unavoidable alternatives like <Link href="https://developers.google.com/recaptcha/">reCAPTCHA</Link>.
</p>
<p>
You can refer to hCaptcha's{" "}
<a href="https://www.hcaptcha.com/privacy" target="_blank" rel="noopener noreferrer">
privacy policy
</a>{" "}
and{" "}
<a href="https://www.hcaptcha.com/terms" target="_blank" rel="noopener noreferrer">
terms of service
</a>{" "}
for more details. While some information is sent to the hCaptcha API about your behavior{" "}
<strong>(on the contact page only)</strong>, at least you won't be helping a certain internet conglomerate{" "}
<a
href="https://blog.cloudflare.com/moving-from-recaptcha-to-hcaptcha/"
target="_blank"
rel="noopener noreferrer"
>
train their self-driving cars
</a>
You can refer to hCaptcha's <Link href="https://www.hcaptcha.com/privacy">privacy policy</Link> and{" "}
<Link href="https://www.hcaptcha.com/terms">terms of service</Link> for more details. While some information is
sent to the hCaptcha API about your behavior <strong>(on the contact page only)</strong>, at least you won't be
helping a certain internet conglomerate{" "}
<Link href="https://blog.cloudflare.com/moving-from-recaptcha-to-hcaptcha/">train their self-driving cars</Link>
. 🚗
</p>
<p>
I also enabled the setting to donate 100% of my{" "}
<a href="https://humanprotocol.org/?lng=en-US" target="_blank" rel="noopener noreferrer">
HMT token
</a>{" "}
earnings to the{" "}
<a href="https://wikimediafoundation.org/" target="_blank" rel="noopener noreferrer">
Wikimedia Foundation
</a>
, for what it's worth. (A few cents, probably... 💰)
<Link href="https://humanprotocol.org/?lng=en-US">HMT token</Link> earnings to the{" "}
<Link href="https://wikimediafoundation.org/">Wikimedia Foundation</Link>, for what it's worth. (A few cents,
probably... 💰)
</p>
</Content>
</>

View File

@ -2,6 +2,7 @@ import { graphql } from "@octokit/graphql";
import { NextSeo } from "next-seo";
import Content from "../components/Content/Content";
import PageTitle from "../components/PageTitle/PageTitle";
import Link from "../components/Link/Link";
import RepositoryCard from "../components/RepositoryCard/RepositoryCard";
import type { GetStaticProps } from "next";
import type { RepoType } from "../types";
@ -31,9 +32,7 @@ const Projects = ({ repos }: Props) => (
</div>
<p className="view_more">
<a href="https://github.com/jakejarvis?tab=repositories" target="_blank" rel="noopener noreferrer">
View more on GitHub...
</a>
<Link href="https://github.com/jakejarvis?tab=repositories">View more on GitHub...</Link>
</p>
</Content>
@ -57,6 +56,8 @@ const Projects = ({ repos }: Props) => (
.view_more {
text-align: center;
margin-bottom: 0;
font-size: 1.1em;
font-weight: 500;
}
`}</style>
</>

File diff suppressed because it is too large Load Diff

View File

@ -1409,31 +1409,31 @@
"@svgr/babel-plugin-transform-react-native-svg" "^6.0.0"
"@svgr/babel-plugin-transform-svg-component" "^6.2.0"
"@svgr/core@^6.2.0":
version "6.2.0"
resolved "https://registry.yarnpkg.com/@svgr/core/-/core-6.2.0.tgz#187a7930695635382c1ab42f476a1d4d45a65994"
integrity sha512-n5PrYAPoTpWGykqa8U05/TVTHOrVR/TxrUJ5EWHP9Db6vR3qnqzwAVLiFT1+slA7zQoJTXafQb+akwThf9SxGw==
"@svgr/core@^6.2.1":
version "6.2.1"
resolved "https://registry.yarnpkg.com/@svgr/core/-/core-6.2.1.tgz#195de807a9f27f9e0e0d678e01084b05c54fdf61"
integrity sha512-NWufjGI2WUyrg46mKuySfviEJ6IxHUOm/8a3Ph38VCWSp+83HBraCQrpEM3F3dB6LBs5x8OElS8h3C0oOJaJAA==
dependencies:
"@svgr/plugin-jsx" "^6.2.0"
"@svgr/plugin-jsx" "^6.2.1"
camelcase "^6.2.0"
cosmiconfig "^7.0.1"
"@svgr/hast-util-to-babel-ast@^6.0.0":
version "6.0.0"
resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.0.0.tgz#423329ad866b6c169009cc82b5e28ffee80c857c"
integrity sha512-S+TxtCdDyRGafH1VG1t/uPZ87aOYOHzWL8kqz4FoSZcIbzWA6rnOmjNViNiDzqmEpzp2PW5o5mZfvC9DiVZhTQ==
"@svgr/hast-util-to-babel-ast@^6.2.1":
version "6.2.1"
resolved "https://registry.yarnpkg.com/@svgr/hast-util-to-babel-ast/-/hast-util-to-babel-ast-6.2.1.tgz#ae065567b74cbe745afae617053adf9a764bea25"
integrity sha512-pt7MMkQFDlWJVy9ULJ1h+hZBDGFfSCwlBNW1HkLnVi7jUhyEXUaGYWi1x6bM2IXuAR9l265khBT4Av4lPmaNLQ==
dependencies:
"@babel/types" "^7.15.6"
entities "^3.0.1"
"@svgr/plugin-jsx@^6.2.0":
version "6.2.0"
resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-6.2.0.tgz#5e41a75b12b34cb66509e63e535606161770ff42"
integrity sha512-QJDEe7K5Hkd4Eewu4pcjiOKTCtjB47Ol6lDLXVhf+jEewi+EKJAaAmM+bNixfW6LSNEg8RwOYQN3GZcprqKfHw==
"@svgr/plugin-jsx@^6.2.1":
version "6.2.1"
resolved "https://registry.yarnpkg.com/@svgr/plugin-jsx/-/plugin-jsx-6.2.1.tgz#5668f1d2aa18c2f1bb7a1fc9f682d3f9aed263bd"
integrity sha512-u+MpjTsLaKo6r3pHeeSVsh9hmGRag2L7VzApWIaS8imNguqoUwDq/u6U/NDmYs/KAsrmtBjOEaAAPbwNGXXp1g==
dependencies:
"@babel/core" "^7.15.5"
"@svgr/babel-preset" "^6.2.0"
"@svgr/hast-util-to-babel-ast" "^6.0.0"
"@svgr/hast-util-to-babel-ast" "^6.2.1"
svg-parser "^2.0.2"
"@svgr/plugin-svgo@^6.2.0":
@ -1445,18 +1445,18 @@
deepmerge "^4.2.2"
svgo "^2.5.0"
"@svgr/webpack@^6.2.0":
version "6.2.0"
resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-6.2.0.tgz#00fafd32e1d59add7b554c40aa2e97e83f975686"
integrity sha512-KlLdGe93A8GDs19g8kjEmHwArgMAP6cUfegr2Nx+yDAYY32IPtjzm3SoqNP+I+cnOF1CToJu1clWTPEmdd8dXg==
"@svgr/webpack@^6.2.1":
version "6.2.1"
resolved "https://registry.yarnpkg.com/@svgr/webpack/-/webpack-6.2.1.tgz#ef5d51c1b6be4e7537fb9f76b3f2b2e22b63c58d"
integrity sha512-h09ngMNd13hnePwgXa+Y5CgOjzlCvfWLHg+MBnydEedAnuLRzUHUJmGS3o2OsrhxTOOqEsPOFt5v/f6C5Qulcw==
dependencies:
"@babel/core" "^7.15.5"
"@babel/plugin-transform-react-constant-elements" "^7.14.5"
"@babel/preset-env" "^7.15.6"
"@babel/preset-react" "^7.14.5"
"@babel/preset-typescript" "^7.15.0"
"@svgr/core" "^6.2.0"
"@svgr/plugin-jsx" "^6.2.0"
"@svgr/core" "^6.2.1"
"@svgr/plugin-jsx" "^6.2.1"
"@svgr/plugin-svgo" "^6.2.0"
"@trysound/sax@0.2.0":
@ -2044,9 +2044,9 @@ camelcase@^6.2.0:
integrity sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==
caniuse-lite@^1.0.30001283, caniuse-lite@^1.0.30001286, caniuse-lite@^1.0.30001297, caniuse-lite@^1.0.30001299:
version "1.0.30001303"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001303.tgz#9b168e4f43ccfc372b86f4bc5a551d9b909c95c9"
integrity sha512-/Mqc1oESndUNszJP0kx0UaQU9kEv9nNtJ7Kn8AdA0mNnH8eR1cj0kG+NbNuC1Wq/b21eA8prhKRA3bbkjONegQ==
version "1.0.30001304"
resolved "https://registry.yarnpkg.com/caniuse-lite/-/caniuse-lite-1.0.30001304.tgz#38af55ed3fc8220cb13e35e6e7309c8c65a05559"
integrity sha512-bdsfZd6K6ap87AGqSHJP/s1V+U6Z5lyrcbBu3ovbCCf8cSYpwTtGrCBObMpJqwxfTbLW6YTIdbb1jEeTelcpYQ==
ccount@^1.0.0:
version "1.1.0"
@ -2541,9 +2541,9 @@ eastasianwidth@^0.2.0:
integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
electron-to-chromium@^1.4.17:
version "1.4.56"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.56.tgz#f660fd2c6739b341d8922fe3a441a5a2804911a1"
integrity sha512-0k/S0FQqRRpJbX7YUjwCcLZ8D42RqGKtaiq90adXBOYgTIWwLA/g3toO8k9yEpqU8iC4QyaWYYWSTBIna8WV4g==
version "1.4.57"
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.57.tgz#2b2766df76ac8dbc0a1d41249bc5684a31849892"
integrity sha512-FNC+P5K1n6pF+M0zIK+gFCoXcJhhzDViL3DRIGy2Fv5PohuSES1JHR7T+GlwxSxlzx4yYbsuzCZvHxcBSRCIOw==
emoji-regex@^8.0.0:
version "8.0.0"
@ -3538,7 +3538,7 @@ internal-slot@^1.0.3:
has "^1.0.3"
side-channel "^1.0.4"
is-absolute-url@^4.0.0:
is-absolute-url@^4.0.1:
version "4.0.1"
resolved "https://registry.yarnpkg.com/is-absolute-url/-/is-absolute-url-4.0.1.tgz#16e4d487d4fded05cfe0685e53ec86804a5e94dc"
integrity sha512-/51/TKE88Lmm7Gc4/8btclNXWS+g50wXhYJq8HWIBAGUBnoAdRu1aXeh364t/O7wXDAcTJDP8PNuNKWUDWie+A==
@ -5681,22 +5681,10 @@ regjsparser@^0.7.0:
dependencies:
jsesc "~0.5.0"
rehype-external-links@^1.0.1:
version "1.0.1"
resolved "https://registry.yarnpkg.com/rehype-external-links/-/rehype-external-links-1.0.1.tgz#9e5d21c606b04d8098012e2d3c811919762d0ba2"
integrity sha512-SrMMtIO2tPLWDfvibhECAn9cuEMW6Fi40ZVK2UQSPTnDEv3LraLzGVyKGb/vrDlUy4RCYTcmm0rq/ss9nPqrsw==
dependencies:
"@types/hast" "^2.0.0"
extend "^3.0.0"
is-absolute-url "^4.0.0"
space-separated-tokens "^2.0.0"
unified "^10.0.0"
unist-util-visit "^4.0.0"
rehype-parse@^8.0.2:
version "8.0.3"
resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-8.0.3.tgz#a1947132a08a930d0c2b6fd2b3dbcc137457c07a"
integrity sha512-RGw0CVt+0S6KdvpE8bbP2Db9WXclQcIX7A0ufM3QFqAhTo/ddJMQrrI2j3cijlRPZlGK8R3pRgC8U5HyV76IDw==
version "8.0.4"
resolved "https://registry.yarnpkg.com/rehype-parse/-/rehype-parse-8.0.4.tgz#3d17c9ff16ddfef6bbcc8e6a25a99467b482d688"
integrity sha512-MJJKONunHjoTh4kc3dsM1v3C9kGrrxvA3U8PxZlP2SjH8RNUSrb+lF7Y0KVaUDnGH2QZ5vAn7ulkiajM9ifuqg==
dependencies:
"@types/hast" "^2.0.0"
hast-util-from-parse5 "^7.0.0"
@ -6022,10 +6010,10 @@ simple-git-hooks@^2.7.0:
resolved "https://registry.yarnpkg.com/simple-git-hooks/-/simple-git-hooks-2.7.0.tgz#121a5c3023663b8abcc5648c8bfe8619dc263705"
integrity sha512-nQe6ASMO9zn5/htIrU37xEIHGr9E6wikXelLbOeTcfsX2O++DHaVug7RSQoq+kO7DvZTH37WA5gW49hN9HTDmQ==
simple-icons@^6.7.0:
version "6.7.0"
resolved "https://registry.yarnpkg.com/simple-icons/-/simple-icons-6.7.0.tgz#45263b5cd624f26f8f11c8ce1cf814be07de41dc"
integrity sha512-bEqOxIkVGTX/Dlq84bXM3ohzRkwE/kpUloSTNvKb7QDTR35ufxMthK3u6kZy2SbmbhFKSpM3Jc8tgAzgfzwV5w==
simple-icons@^6.8.0:
version "6.8.0"
resolved "https://registry.yarnpkg.com/simple-icons/-/simple-icons-6.8.0.tgz#ee837f4222dc00b600d90ec0025f52ea44c2c749"
integrity sha512-Ow1ISgCpRZuXa1xvwVqUrep6xqdtNmcgqd9pCtMgni7m1XmQI56EuSSJYAbyvCcoakXVcbtHFajtPXEd86iMCA==
sirv@^1.0.7:
version "1.0.19"