1
mirror of https://github.com/jakejarvis/jarv.is.git synced 2025-09-16 21:05:33 -04:00

fresh <PageTitle> look

This commit is contained in:
2025-03-08 13:09:29 -05:00
parent 5cfa86f690
commit 2d6fc82f71
29 changed files with 346 additions and 401 deletions

View File

@@ -1,4 +1,3 @@
import Content from "../../components/Content";
import PageTitle from "../../components/PageTitle";
import Video from "../../components/Video";
import { metadata as defaultMetadata } from "../layout";
@@ -24,11 +23,9 @@ export const metadata: Metadata = {
export default function Page() {
return (
<>
<PageTitle>📼 1996.MOV</PageTitle>
<PageTitle canonical="/birthday">1996.mov</PageTitle>
<Content>
<Video src={["/static/birthday/birthday.webm", "/static/birthday/birthday.mp4"]} poster={thumbnail.src} />
</Content>
</>
);
}

View File

@@ -1,5 +1,4 @@
import PageTitle from "../../components/PageTitle";
import Content from "../../components/Content";
import { metadata as defaultMetadata } from "../layout";
import featuredImage from "./screenshot.png";
@@ -19,9 +18,7 @@ export const metadata = {
},
};
<PageTitle>🤖 CLI</PageTitle>
<Content>
<PageTitle canonical="/cli">CLI</PageTitle>
> The [Jake Jarvis](https://jarv.is/) CLI (aka the most useless Node module ever published, in history, by anyone, ever).
@@ -48,5 +45,3 @@ npx @jakejarvis/cli
## License
MIT © [Jake Jarvis](https://jarv.is/), [Sindre Sorhus](https://sindresorhus.com/)
</Content>

View File

@@ -1,4 +1,3 @@
import Content from "../../components/Content";
import PageTitle from "../../components/PageTitle";
import Link from "../../components/Link";
import ContactForm from "./form";
@@ -7,6 +6,7 @@ import type { Metadata, Route } from "next";
export const metadata: Metadata = {
title: "Contact Me",
description: "Fill out this quick form and I'll get back to you as soon as I can.",
openGraph: {
...defaultMetadata.openGraph,
title: "Contact Me",
@@ -20,15 +20,14 @@ export const metadata: Metadata = {
export default function Page() {
return (
<>
<PageTitle>📬 Contact Me</PageTitle>
<Content
<div
style={{
maxWidth: "600px",
margin: "0 auto",
}}
>
<PageTitle canonical="/contact">Contact</PageTitle>
<p>
Fill out this quick form and I'll get back to you as soon as I can! You can also{" "}
<Link href="mailto:jake@jarv.is">email me directly</Link> or send me a{" "}
@@ -45,7 +44,6 @@ export default function Page() {
</p>
<ContactForm />
</Content>
</>
</div>
);
}

View File

@@ -14,8 +14,7 @@ pre {
/* https://css-tricks.com/almanac/rules/m/media/prefers-reduced-motion/ */
@media (prefers-reduced-motion: reduce) {
* {
animation-duration: 0.001ms !important;
animation-iteration-count: 1 !important;
transition-duration: 0.001ms !important;
animation: none !important;
transition: none !important;
}
}

View File

@@ -1,4 +1,3 @@
import Content from "../../components/Content";
import PageTitle from "../../components/PageTitle";
import Link from "../../components/Link";
import Video from "../../components/Video";
@@ -25,9 +24,8 @@ export const metadata: Metadata = {
export default function Page() {
return (
<>
<PageTitle>My Brief Apperance in Hillary Clinton's DNC Video</PageTitle>
<PageTitle canonical="/hillary">HRC.mov</PageTitle>
<Content>
<Video
src={[
"/static/hillary/convention-720p.webm",
@@ -60,7 +58,6 @@ export default function Page() {
</Link>
. &copy; 2016.
</p>
</Content>
</>
);
}

View File

@@ -7,6 +7,9 @@
.default {
width: 100%;
padding: 1.5em;
font-size: 0.9em;
line-height: 1.7;
color: var(--colors-text);
}
.container {
@@ -14,3 +17,10 @@
margin: 0 auto;
display: block;
}
@media (max-width: 768px) {
.default {
font-size: 0.925em;
line-height: 1.85;
}
}

View File

@@ -1,4 +1,3 @@
import Content from "../../components/Content";
import PageTitle from "../../components/PageTitle";
import Link from "../../components/Link";
import Video from "../../components/Video";
@@ -25,13 +24,9 @@ export const metadata: Metadata = {
export default function Page() {
return (
<>
<PageTitle>Facebook App on "The Lab with Leo Laporte"</PageTitle>
<PageTitle canonical="/leo">TheLab.mov</PageTitle>
<Content>
<Video
src={["/static/leo/leo.webm", "/static/leo/leo.mp4", "/static/leo/subs.en.vtt"]}
poster={thumbnail.src}
/>
<Video src={["/static/leo/leo.webm", "/static/leo/leo.mp4", "/static/leo/subs.en.vtt"]} poster={thumbnail.src} />
<p
style={{
@@ -52,7 +47,6 @@ export default function Page() {
</Link>
. &copy; 2007 G4 Media, Inc.
</p>
</Content>
</>
);
}

View File

@@ -1,5 +1,4 @@
import PageTitle from "../../components/PageTitle";
import Content from "../../components/Content";
import { metadata as defaultMetadata } from "../layout";
export const metadata = {
@@ -15,9 +14,7 @@ export const metadata = {
},
};
<PageTitle>📜 License</PageTitle>
<Content>
<PageTitle canonical="/license">License</PageTitle>
Unless otherwise noted, content on this website is published under the [**Creative Commons Attribution 4.0 International Public License**](https://creativecommons.org/licenses/by/4.0/) (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).
@@ -196,5 +193,3 @@ d. Nothing in this Public License constitutes or may be interpreted as a limitat
> 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 [_CC0 Public Domain Dedication_](https://creativecommons.org/publicdomain/zero/1.0/legalcode). 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 [creativecommons.org/policies](http://creativecommons.org/policies), 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.
>
> Creative Commons may be contacted at [creativecommons.org](https://creativecommons.org/).
</Content>

View File

@@ -1,7 +1,7 @@
.meta {
display: inline-flex;
flex-wrap: wrap;
font-size: 0.825em;
font-size: 0.925em;
line-height: 2.3;
letter-spacing: 0.04em;
color: var(--colors-medium);
@@ -46,7 +46,7 @@
.title {
margin: 0.3em 0 0.5em -1px; /* misaligned left margin, super nitpicky */
font-size: 2.1em;
font-size: 2.3em;
line-height: 1.3;
font-weight: 700;
}
@@ -68,6 +68,6 @@
@media (max-width: 768px) {
.title {
font-size: 1.8em;
font-size: 1.9em;
}
}

View File

@@ -1,6 +1,5 @@
import { Suspense } from "react";
import { ErrorBoundary } from "react-error-boundary";
import Content from "../../../components/Content";
import Link from "../../../components/Link";
import Time from "../../../components/Time";
import Comments from "../../../components/Comments";
@@ -145,9 +144,7 @@ export default async function Page({ params }: { params: Promise<{ slug: string
/>
</h1>
<Content>
<MDXContent />
</Content>
{!frontmatter.noComments && (
<div id="comments" className={styles.comments}>

View File

@@ -5,10 +5,10 @@
}
.section:first-of-type {
margin-top: 0;
margin-top: 0.25em;
}
.section:last-of-type {
margin-bottom: 0;
margin-bottom: 0.25em;
}
.year {

View File

@@ -1,4 +1,3 @@
import Content from "../../components/Content";
import Link from "../../components/Link";
import Time from "../../components/Time";
import { getAllPosts } from "../../lib/helpers/posts";
@@ -59,5 +58,5 @@ export default async function Page() {
// grouped posts enter this component ordered chronologically -- we want reverse chronological
const reversed = sections.reverse();
return <Content>{reversed}</Content>;
return <>{reversed}</>;
}

View File

@@ -1,22 +1,22 @@
.page h1 {
margin: 0 0 0.5em -1px; /* misaligned left margin, super nitpicky */
font-size: 1.75em;
font-size: 1.925em;
font-weight: 500;
line-height: 1.1;
line-height: 1.2;
color: var(--colors-text);
}
.page h2 {
margin: 0.5em 0 0.5em -1px;
font-size: 1.2em;
font-size: 1.3em;
font-weight: 400;
line-height: 1.4;
line-height: 1.5;
color: var(--colors-text);
}
.page p {
margin: 0.85em 0;
font-size: 0.95em;
font-size: 1.05em;
line-height: 1.7;
color: var(--colors-text);
}
@@ -74,15 +74,15 @@
@media (max-width: 768px) {
.page h1 {
font-size: 1.6em;
font-size: 1.8em;
}
.page h2 {
font-size: 1.25em;
font-size: 1.3em;
}
.page p {
font-size: 0.925em;
line-height: 1.825;
font-size: 1em;
line-height: 1.9;
}
}

View File

@@ -1,8 +1,9 @@
.wackyWrapper {
font-weight: 700;
font-size: 1em;
text-align: center;
font-size: 1.15em;
}
body:has(.wackyWrapper) {
/* classic windows 9x cursor easter egg */
cursor:
url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAYAAABzenr0AAAAZklEQVR4AWIAgn/uBT6A9uoAAwAQiIJo97/0Rgy0ANoJH8MPeEgtqwPQEACqCoQHAKECQKgAECoAhAoAoQJAqAAQxh1oPQfcW3kJpxHtL1AAHAwEwwdYiH8BIEgBTBRAAAEEEEAAG7mRt30hEhoLAAAAAElFTkSuQmCC")
@@ -10,7 +11,7 @@
auto;
}
.wackyWrapper a {
body:has(.wackyWrapper) a {
/* windows 9x hand cursor */
cursor:
url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgAgMAAAAOFJJnAAAACVBMVEVHcEwAAAD///8W1S+BAAAAAXRSTlMAQObYZgAAAEdJREFUeAFjoAVghTGkHIhghMAYmQEwxlIYYxlYlSiQMQEsELUKyli1ahWYwQZjMGIwGLKQGA4QA1EYEP0rGVAZrKGhSF4BAHw/HsVwshytAAAAAElFTkSuQmCC")

View File

@@ -1,4 +1,3 @@
import Content from "../../components/Content";
import PageTitle from "../../components/PageTitle";
import Link from "../../components/Link";
import Figure from "../../components/Figure";
@@ -41,24 +40,22 @@ export const metadata: Metadata = {
export default async function Page() {
return (
<>
<PageTitle>🕰 Previously on...</PageTitle>
<Content
<div
className={styles.wackyWrapper}
style={{
fontFamily: `${ComicNeue.style.fontFamily}, var(--fonts-sans)`,
}}
>
<PageTitle canonical="/previously">Previously</PageTitle>
<Figure src={img_wayback} alt="Timeline of this website's past." priority className={styles.screenshot}>
...the{" "}
<Link href="https://web.archive.org/web/20010501000000*/jakejarvis.com">Cringey Chronicles&trade;</Link> of
this website's past.
...the <Link href="https://web.archive.org/web/20010501000000*/jakejarvis.com">Cringey Chronicles&trade;</Link>{" "}
of this website's past.
</Figure>
<HorizontalRule className={styles.divider} />
<p style={{ marginBottom: "0.5em" }}>
<p style={{ textAlign: "center", margin: "0.5em 0" }}>
🚨 Trigger warning: excessive marquees, animated GIFs, Comic Sans, popups,{" "}
<CodeInline
style={{
@@ -71,7 +68,7 @@ export default async function Page() {
ahead...
</p>
<p style={{ fontSize: "0.95em", marginBottom: "0.5em" }}>
<p style={{ textAlign: "center", fontSize: "0.95em", margin: "0.5em 0" }}>
<Link href="/y2k">
<svg
fill="currentColor"
@@ -191,7 +188,6 @@ export default async function Page() {
<Link href="https://quiet-truffle-92842d.netlify.app/">March 2020</Link> (
<Link href="https://github.com/jakejarvis/jarv.is-hugo">view source</Link>)
</Figure>
</Content>
</>
</div>
);
}

View File

@@ -1,5 +1,4 @@
import PageTitle from "../../components/PageTitle";
import Content from "../../components/Content";
import { metadata as defaultMetadata } from "../layout";
export const metadata = {
@@ -15,9 +14,7 @@ export const metadata = {
},
};
<PageTitle>🕵️ Privacy</PageTitle>
<Content>
<PageTitle canonical="/privacy">Privacy</PageTitle>
Okay, this is an easy one. 😉
@@ -52,5 +49,3 @@ Occasionally, embedded content from third-party services is included in posts, a
Using [**Cloudflare Turnstile**](https://www.cloudflare.com/products/turnstile/) to fight bot spam on the [contact form](/contact) was an easy choice over seemingly unavoidable alternatives like [reCAPTCHA](https://developers.google.com/recaptcha/).
You can refer to Cloudflare's [privacy policy](https://www.cloudflare.com/privacypolicy/) and [terms of service](https://www.cloudflare.com/website-terms/) for more details. While some information is sent to the Turnstile API about your behavior (on the contact page only), at least you won't be helping a certain internet conglomerate [train their self-driving cars](https://blog.cloudflare.com/moving-from-recaptcha-to-hcaptcha/). 🚗
</Content>

View File

@@ -5,16 +5,16 @@
align-items: flex-start;
width: 100%;
line-height: 1.1;
gap: 1em;
}
.card {
flex-grow: 1;
margin: 0.6em;
width: 370px;
padding: 1.2em 1.2em 0.8em 1.2em;
border: 1px solid var(--colors-kindaLight);
border-radius: var(--radii-corner);
font-size: 0.85em;
font-size: 0.9em;
color: var(--colors-mediumDark);
transition: border var(--transitions-fade);
}

View File

@@ -1,5 +1,4 @@
import { graphql } from "@octokit/graphql";
import Content from "../../components/Content";
import PageTitle from "../../components/PageTitle";
import Link from "../../components/Link";
import RelativeTime from "../../components/RelativeTime";
@@ -111,9 +110,8 @@ export default async function Page() {
return (
<>
<PageTitle>💾 Projects</PageTitle>
<PageTitle canonical="/projects">Projects</PageTitle>
<Content>
<div className={styles.grid}>
{repos?.map((repo) => (
<div key={repo.name} className={styles.card}>
@@ -198,7 +196,6 @@ export default async function Page() {
GitHub...
</Link>
</p>
</Content>
</>
);
}

View File

@@ -6,6 +6,7 @@ export const dynamic = "force-static";
const robots = (): MetadataRoute.Robots => {
// I'm already _so_ over this shit...
// https://github.com/ai-robots-txt/ai.robots.txt/blob/main/robots.txt
// TODO: dynamically fetch this list from the above repo.
const naughtySpiders = [
"AI2Bot",
"Ai2Bot-Dolma",
@@ -13,6 +14,7 @@ const robots = (): MetadataRoute.Robots => {
"anthropic-ai",
"Applebot",
"Applebot-Extended",
"Brightbot 1.0",
"Bytespider",
"CCBot",
"ChatGPT-User",

View File

@@ -11,33 +11,30 @@ const sitemap = async (): Promise<MetadataRoute.Sitemap> => {
const routes: MetadataRoute.Sitemap = [
{
// homepage
url: config.baseUrl,
url: `${config.baseUrl}/`,
priority: 1.0,
changeFrequency: "weekly",
lastModified: new Date(process.env.RELEASE_DATE || Date.now()), // timestamp frozen when a new build is deployed
},
{ url: `${config.baseUrl}/tweets/` },
{ url: `${config.baseUrl}/y2k/` },
];
// add each directory in the app folder as a route (excluding special routes)
const appDir = path.resolve(process.cwd(), "app");
(
await glob("*", {
await glob("**/page.{tsx,mdx}", {
cwd: appDir,
deep: 0,
onlyDirectories: true,
markDirectories: true,
ignore: [
// don't include special routes, see: https://nextjs.org/docs/app/api-reference/file-conventions/metadata
"api",
"feed.atom",
"feed.xml",
// homepage already included manually above
"page.tsx",
// don't include dynamic routes
"notes/[slug]/page.tsx",
],
})
).forEach((route) => {
routes.push({
// make all URLs absolute
url: `${config.baseUrl}/${route}`,
// remove matching page.(tsx|mdx) file and make all URLs absolute
url: `${config.baseUrl}/${route.replace(/page\.(tsx|mdx)$/, "")}`,
});
});
@@ -49,7 +46,10 @@ const sitemap = async (): Promise<MetadataRoute.Sitemap> => {
});
});
return routes;
// sort alphabetically by URL, sometimes fast-glob returns results in a different order
routes.sort((a, b) => (a.url < b.url ? -1 : 1));
return [...routes];
};
export default sitemap;

View File

@@ -1,5 +1,4 @@
import PageTitle from "../../components/PageTitle";
import Content from "../../components/Content";
import { metadata as defaultMetadata } from "../layout";
import featuredImage from "./desktop.png";
@@ -19,9 +18,7 @@ export const metadata = {
},
};
<PageTitle>/uses</PageTitle>
<Content>
<PageTitle canonical="/uses">Uses</PageTitle>
~~I regularly get messages asking about which tools I use to work.~~
@@ -178,5 +175,3 @@ Other geeky stuff:
- 2x [**ecobee3 lite**](https://www.ecobee.com/en-us/smart-thermostats/smart-wifi-thermostat/)
- 2x [**Sonos One**](https://www.sonos.com/en-us/shop/one.html) (with Alexa turned off...hopefully? 🤫)
- 2x [**Apple TV 4K** (2021)](https://www.apple.com/apple-tv-4k/)
</Content>

View File

@@ -1,4 +1,3 @@
import Content from "../../components/Content";
import Link from "../../components/Link";
import CodeBlock from "../../components/CodeBlock/CodeBlock";
import { metadata as defaultMetadata } from "../layout";
@@ -22,7 +21,7 @@ export const metadata: Metadata = {
export default async function Page() {
return (
<Content
<div
style={{
backgroundImage: `url(${backgroundImg.src})`,
backgroundRepeat: "repeat",
@@ -72,6 +71,6 @@ export default async function Page() {
<span style={{ color: "var(--colors-codeAttribute)" }}>~</span>${" "}
<span style={{ color: "var(--colors-codeLiteral)" }}>reboot</span> 0
</CodeBlock>
</Content>
</div>
);
}

View File

@@ -1,12 +0,0 @@
.content {
font-size: 0.9em;
line-height: 1.7;
color: var(--colors-text);
}
@media (max-width: 768px) {
.content {
font-size: 0.925em;
line-height: 1.85;
}
}

View File

@@ -1,10 +0,0 @@
import clsx from "clsx";
import type { ComponentPropsWithoutRef } from "react";
import styles from "./Content.module.css";
const Content = ({ className, ...rest }: ComponentPropsWithoutRef<"div">) => (
<div className={clsx(styles.content, className)} {...rest} />
);
export default Content;

View File

@@ -1,2 +0,0 @@
export * from "./Content";
export { default } from "./Content";

View File

@@ -1,17 +1,21 @@
.title {
margin-top: 0;
margin-bottom: 0.6em;
font-size: 1.7em;
font-size: 1.9em;
font-weight: 600;
text-align: center;
text-transform: lowercase;
letter-spacing: 0.02em;
}
.link {
color: var(--colors-text) !important;
.slug::before {
content: "\002E\002F"; /* "./" */
letter-spacing: 0.1em;
color: var(--colors-mediumLight) !important;
margin-right: -0.1em;
}
@media (max-width: 768px) {
.title {
font-size: 1.8em;
font-size: 2em;
}
}

View File

@@ -1,6 +1,3 @@
"use client";
import { usePathname } from "next/navigation";
import clsx from "clsx";
import Link from "../Link";
import type { ComponentPropsWithoutRef } from "react";
@@ -8,14 +5,14 @@ import type { Route } from "next";
import styles from "./PageTitle.module.css";
export type PageTitleProps = ComponentPropsWithoutRef<"h1">;
const PageTitle = ({ className, children, ...rest }: PageTitleProps) => {
const pathname = usePathname() || "";
export type PageTitleProps = ComponentPropsWithoutRef<"h1"> & {
canonical: string;
};
const PageTitle = ({ canonical, className, children, ...rest }: PageTitleProps) => {
return (
<h1 className={clsx(styles.title, className)} {...rest}>
<Link href={pathname as Route} plain className={styles.link}>
<Link href={canonical as Route} plain className={styles.slug}>
{children}
</Link>
</h1>

View File

@@ -1,3 +1,3 @@
.wrapper :global(lite-youtube) {
.wrapper lite-youtube {
margin: 0 auto;
}

View File

@@ -19,5 +19,7 @@ export function middleware(request: NextRequest) {
export const config = {
// save compute time by skipping middleware for static and metadata files
matcher: ["/((?!_next/static|_next/image|_vercel|static|favicon.ico).*)"],
matcher: [
"/((?!_next/static|_next/image|_vercel/|static|.well-known|favicon.ico|icon.png|apple-icon.png|manifest.webmanifest).*)",
],
};