mirror of
https://github.com/jakejarvis/jarv.is.git
synced 2025-09-13 23:55:35 -04:00
extract syntax highlighting styles into a CSS module
This commit is contained in:
@@ -61,7 +61,7 @@
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.content figure > :global(.image_wrapper) {
|
||||
.content figure :global(.image_wrapper) {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
|
66
components/home/ColorLink.tsx
Normal file
66
components/home/ColorLink.tsx
Normal file
@@ -0,0 +1,66 @@
|
||||
import { memo } from "react";
|
||||
import Link from "next/link";
|
||||
import css from "styled-jsx/css";
|
||||
import isAbsoluteUrl from "is-absolute-url";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
type ColorLinkProps = {
|
||||
children: ReactNode;
|
||||
href: string;
|
||||
lightColor: string;
|
||||
darkColor: string;
|
||||
title?: string;
|
||||
external?: boolean;
|
||||
};
|
||||
|
||||
const getFancyLinkStyles = ({ lightColor, darkColor }: Partial<ColorLinkProps>) => {
|
||||
// spits out a linear-gradient (that's not realy a gradient) with translucent color in rgba() format
|
||||
const linearGradient = (hex: string, alpha = 0.4) => {
|
||||
// hex -> rgb, adapted from https://github.com/sindresorhus/hex-rgb/blob/main/index.js
|
||||
hex = hex.replace(/^#/, "");
|
||||
const number = Number.parseInt(hex, 16);
|
||||
const red = number >> 16;
|
||||
const green = (number >> 8) & 255;
|
||||
const blue = number & 255;
|
||||
|
||||
const rgbaString = `rgba(${red}, ${green}, ${blue}, ${alpha})`;
|
||||
|
||||
return `linear-gradient(${rgbaString}, ${rgbaString})`;
|
||||
};
|
||||
|
||||
return css.resolve`
|
||||
a {
|
||||
color: ${lightColor};
|
||||
background-image: ${linearGradient(lightColor)};
|
||||
}
|
||||
:global([data-theme="dark"]) a {
|
||||
color: ${darkColor};
|
||||
background-image: ${linearGradient(darkColor)};
|
||||
}
|
||||
`;
|
||||
};
|
||||
|
||||
const ColorLink = ({ href, title, lightColor, darkColor, external = false, children }: ColorLinkProps) => {
|
||||
external = external || isAbsoluteUrl(href);
|
||||
|
||||
const { className, styles } = getFancyLinkStyles({ lightColor, darkColor });
|
||||
|
||||
return (
|
||||
<>
|
||||
<Link href={href} passHref={true} prefetch={false}>
|
||||
<a
|
||||
className={className}
|
||||
title={title}
|
||||
target={external ? "_blank" : undefined}
|
||||
rel={external ? "noopener noreferrer" : undefined}
|
||||
>
|
||||
{children}
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
{styles}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
export default memo(ColorLink);
|
@@ -38,10 +38,9 @@ const Loading = ({ width, boxes = 3, timing = 0.1 }: Props) => {
|
||||
// width of each box correlates with number of boxes (with a little padding)
|
||||
// each individual box's animation has a staggered start in corresponding order
|
||||
divs.push(
|
||||
<>
|
||||
<div key={i} className={boxClassName} style={{ animationDelay: `${i * timing}s` }} />
|
||||
<div key={i} className={boxClassName} style={{ animationDelay: `${i * timing}s` }}>
|
||||
{boxStyles}
|
||||
</>
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
|
98
components/media/Code.module.css
Normal file
98
components/media/Code.module.css
Normal file
@@ -0,0 +1,98 @@
|
||||
.code_block {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
overflow-x: scroll;
|
||||
margin: 1em auto;
|
||||
}
|
||||
|
||||
/* the following sub-classes MUST be global -- the highlight rehype plugin isn't aware of this file */
|
||||
|
||||
.code_block :global(.code-highlight) {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 1em;
|
||||
tab-size: 2;
|
||||
color: var(--code-text);
|
||||
background-color: var(--code-background);
|
||||
}
|
||||
|
||||
/* leave room for clipboard button to the right of the first line */
|
||||
.code_block :global(.code-highlight) > :global(.code-line:first-of-type) {
|
||||
margin-right: 3em;
|
||||
}
|
||||
|
||||
.code_block :global(.code-highlight) > :global(.code-line.line-number::before) {
|
||||
display: inline-block;
|
||||
width: 1.5em;
|
||||
margin-right: 1.5em;
|
||||
text-align: right;
|
||||
color: var(--code-comment);
|
||||
content: attr(line); /* added to spans by prism */
|
||||
}
|
||||
|
||||
.code_block :global(.code-highlight) :global(.token.comment),
|
||||
.code_block :global(.code-highlight) :global(.token.prolog),
|
||||
.code_block :global(.code-highlight) :global(.token.cdata) {
|
||||
color: var(--code-comment);
|
||||
}
|
||||
|
||||
.code_block :global(.code-highlight) :global(.token.delimiter),
|
||||
.code_block :global(.code-highlight) :global(.token.boolean),
|
||||
.code_block :global(.code-highlight) :global(.token.keyword),
|
||||
.code_block :global(.code-highlight) :global(.token.selector),
|
||||
.code_block :global(.code-highlight) :global(.token.important),
|
||||
.code_block :global(.code-highlight) :global(.token.doctype),
|
||||
.code_block :global(.code-highlight) :global(.token.atrule),
|
||||
.code_block :global(.code-highlight) :global(.token.url) {
|
||||
color: var(--code-keyword);
|
||||
}
|
||||
|
||||
.code_block :global(.code-highlight) :global(.token.tag),
|
||||
.code_block :global(.code-highlight) :global(.token.builtin),
|
||||
.code_block :global(.code-highlight) :global(.token.regex) {
|
||||
color: var(--code-namespace);
|
||||
}
|
||||
|
||||
.code_block :global(.code-highlight) :global(.token.property),
|
||||
.code_block :global(.code-highlight) :global(.token.constant),
|
||||
.code_block :global(.code-highlight) :global(.token.variable),
|
||||
.code_block :global(.code-highlight) :global(.token.attr-value),
|
||||
.code_block :global(.code-highlight) :global(.token.class-name),
|
||||
.code_block :global(.code-highlight) :global(.token.string),
|
||||
.code_block :global(.code-highlight) :global(.token.char) {
|
||||
color: var(--code-variable);
|
||||
}
|
||||
|
||||
.code_block :global(.code-highlight) :global(.token.literal-property),
|
||||
.code_block :global(.code-highlight) :global(.token.attr-name) {
|
||||
color: var(--code-attribute);
|
||||
}
|
||||
|
||||
.code_block :global(.code-highlight) :global(.token.function) {
|
||||
color: var(--code-literal);
|
||||
}
|
||||
|
||||
.code_block :global(.code-highlight) :global(.token.tag .punctuation),
|
||||
.code_block :global(.code-highlight) :global(.token.attr-value .punctuation) {
|
||||
color: var(--code-punctuation);
|
||||
}
|
||||
|
||||
.code_block :global(.code-highlight) :global(.token.inserted) {
|
||||
background-color: var(--code-addition);
|
||||
}
|
||||
|
||||
.code_block :global(.code-highlight) :global(.token.deleted) {
|
||||
background-color: var(--code-deletion);
|
||||
}
|
||||
|
||||
.code_block :global(.code-highlight) :global(.token.url) {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.code_block :global(.code-highlight) :global(.token.bold) {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.code_block :global(.code-highlight) :global(.token.italic) {
|
||||
font-style: italic;
|
||||
}
|
@@ -1,6 +1,8 @@
|
||||
import dynamic from "next/dynamic";
|
||||
import CopyButton from "../clipboard/CopyButton";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
import styles from "./Code.module.css";
|
||||
|
||||
type CustomCodeProps = {
|
||||
className?: string;
|
||||
children: ReactNode;
|
||||
@@ -8,114 +10,13 @@ type CustomCodeProps = {
|
||||
|
||||
const CustomCode = (props: CustomCodeProps) => {
|
||||
if (props.className?.split(" ").includes("code-highlight")) {
|
||||
const CopyButton = dynamic(() => import("../clipboard/CopyButton"));
|
||||
|
||||
// full multi-line code blocks with prism highlighting and copy-to-clipboard button
|
||||
return (
|
||||
<>
|
||||
<div className="code-block">
|
||||
<div className={styles.code_block}>
|
||||
<CopyButton source={props.children} />
|
||||
<code {...props}>{props.children}</code>
|
||||
</div>
|
||||
|
||||
<style jsx global>{`
|
||||
.code-block {
|
||||
position: relative;
|
||||
width: 100%;
|
||||
overflow-x: scroll;
|
||||
margin: 1em auto;
|
||||
}
|
||||
|
||||
.code-highlight {
|
||||
display: block;
|
||||
overflow-x: auto;
|
||||
padding: 1em;
|
||||
tab-size: 2;
|
||||
color: var(--code-text);
|
||||
background-color: var(--code-background);
|
||||
}
|
||||
|
||||
/* leave room for clipboard button to the right of the first line */
|
||||
.code-highlight > .code-line:first-of-type {
|
||||
margin-right: 3em;
|
||||
}
|
||||
|
||||
.code-highlight > .code-line.line-number::before {
|
||||
display: inline-block;
|
||||
width: 1.5em;
|
||||
margin-right: 1.5em;
|
||||
text-align: right;
|
||||
color: var(--code-comment);
|
||||
content: attr(line); /* added to spans by prism */
|
||||
}
|
||||
|
||||
.code-highlight .token.comment,
|
||||
.code-highlight .token.prolog,
|
||||
.code-highlight .token.cdata {
|
||||
color: var(--code-comment);
|
||||
}
|
||||
|
||||
.code-highlight .token.delimiter,
|
||||
.code-highlight .token.boolean,
|
||||
.code-highlight .token.keyword,
|
||||
.code-highlight .token.selector,
|
||||
.code-highlight .token.important,
|
||||
.code-highlight .token.doctype,
|
||||
.code-highlight .token.atrule,
|
||||
.code-highlight .token.url {
|
||||
color: var(--code-keyword);
|
||||
}
|
||||
|
||||
.code-highlight .token.tag,
|
||||
.code-highlight .token.builtin,
|
||||
.code-highlight .token.regex {
|
||||
color: var(--code-namespace);
|
||||
}
|
||||
|
||||
.code-highlight .token.property,
|
||||
.code-highlight .token.constant,
|
||||
.code-highlight .token.variable,
|
||||
.code-highlight .token.attr-value,
|
||||
.code-highlight .token.class-name,
|
||||
.code-highlight .token.string,
|
||||
.code-highlight .token.char {
|
||||
color: var(--code-variable);
|
||||
}
|
||||
|
||||
.code-highlight .token.literal-property,
|
||||
.code-highlight .token.attr-name {
|
||||
color: var(--code-attribute);
|
||||
}
|
||||
|
||||
.code-highlight .token.function {
|
||||
color: var(--code-literal);
|
||||
}
|
||||
|
||||
.code-highlight .token.tag .punctuation,
|
||||
.code-highlight .token.attr-value .punctuation {
|
||||
color: var(--code-punctuation);
|
||||
}
|
||||
|
||||
.code-highlight .token.inserted {
|
||||
background-color: var(--code-addition);
|
||||
}
|
||||
|
||||
.code-highlight .token.deleted {
|
||||
background-color: var(--code-deletion);
|
||||
}
|
||||
|
||||
.code-highlight .token.url {
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
.code-highlight .token.bold {
|
||||
font-weight: bold;
|
||||
}
|
||||
|
||||
.code-highlight .token.italic {
|
||||
font-style: italic;
|
||||
}
|
||||
`}</style>
|
||||
</>
|
||||
);
|
||||
} else {
|
||||
|
@@ -22,7 +22,7 @@
|
||||
}
|
||||
|
||||
.view_source {
|
||||
padding-bottom: 2px;
|
||||
padding-bottom: 2px !important;
|
||||
border-bottom: 1px solid;
|
||||
border-color: var(--light);
|
||||
}
|
||||
|
@@ -39,11 +39,10 @@
|
||||
"feed": "^4.2.2",
|
||||
"formik": "^2.2.9",
|
||||
"gray-matter": "^4.0.3",
|
||||
"hex-rgb": "^5.0.0",
|
||||
"html-escaper": "^3.0.3",
|
||||
"is-absolute-url": "^4.0.1",
|
||||
"is-email-like": "^2.0.0",
|
||||
"marked": "^4.0.9",
|
||||
"marked": "^4.0.10",
|
||||
"mdx-bundler": "^8.0.1",
|
||||
"modern-normalize": "github:sindresorhus/modern-normalize#1fc6b5a86676b7ac8abc62d04d6080f92debc70f",
|
||||
"next": "v12.0.8",
|
||||
|
@@ -1,44 +1,5 @@
|
||||
import Link from "next/link";
|
||||
import isAbsoluteUrl from "is-absolute-url";
|
||||
import hexRgb from "hex-rgb";
|
||||
import ColorLink from "../components/home/ColorLink";
|
||||
import { WaveIcon, LockIcon } from "../components/icons";
|
||||
import type { ReactNode } from "react";
|
||||
|
||||
type ColorLinkProps = {
|
||||
children: ReactNode;
|
||||
href: string;
|
||||
lightColor: string;
|
||||
darkColor: string;
|
||||
title?: string;
|
||||
external?: boolean;
|
||||
};
|
||||
const ColorLink = ({ href, title, lightColor, darkColor, external = false, children }: ColorLinkProps) => {
|
||||
external = external || isAbsoluteUrl(href);
|
||||
|
||||
// spits out a translucent color in rgba() format that's compatible with linear-gradient()
|
||||
const hexToRgba = (hex: string, alpha = 0.4) => hexRgb(hex, { alpha, format: "css" });
|
||||
|
||||
return (
|
||||
<>
|
||||
<Link href={href} passHref={true} prefetch={false}>
|
||||
<a title={title} target={external ? "_blank" : undefined} rel={external ? "noopener noreferrer" : undefined}>
|
||||
{children}
|
||||
</a>
|
||||
</Link>
|
||||
|
||||
<style jsx>{`
|
||||
a {
|
||||
color: ${lightColor};
|
||||
background-image: linear-gradient(${hexToRgba(lightColor)}, ${hexToRgba(lightColor)});
|
||||
}
|
||||
:global([data-theme="dark"]) a {
|
||||
color: ${darkColor};
|
||||
background-image: linear-gradient(${hexToRgba(darkColor)}, ${hexToRgba(darkColor)});
|
||||
}
|
||||
`}</style>
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
||||
const Index = () => (
|
||||
<>
|
||||
@@ -331,8 +292,9 @@ const Index = () => (
|
||||
color: var(--medium-light);
|
||||
}
|
||||
.birthday :global(a:hover) {
|
||||
cursor: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns=%27http://www.w3.org/2000/svg%27 viewBox=%270 0 36 36%27 width=%2720%27 height=%2720%27%3E%3Cg fill=%27none%27%3E%3Cpath fill=%27%23292F33%27 d=%27m2.651 6.073 26.275 26.276c.391.391 2.888-2.107 2.497-2.497L5.148 3.576c-.39-.391-2.888 2.107-2.497 2.497z%27/%3E%3Cpath fill=%27%2366757F%27 d=%27M29.442 31.23 3.146 4.934l.883-.883 26.296 26.296z%27/%3E%3Cpath fill=%27%23E1E8ED%27 d=%27m33.546 33.483-.412.412-.671.671a.967.967 0 0 1-.255.169.988.988 0 0 1-1.159-.169l-2.102-2.102.495-.495.883-.883 1.119-1.119 2.102 2.102a.999.999 0 0 1 0 1.414zM4.029 4.79l-.883.883-.495.495L.442 3.96a.988.988 0 0 1-.169-1.159.967.967 0 0 1 .169-.255l.671-.671.412-.412a.999.999 0 0 1 1.414 0l2.208 2.208L4.029 4.79z%27/%3E%3Cpath fill=%27%23F5F8FA%27 d=%27m30.325 30.497 2.809 2.809-.671.671a.967.967 0 0 1-.255.169l-2.767-2.767.884-.882zM3.146 5.084.273 2.211a.967.967 0 0 1 .169-.255l.671-.671 2.916 2.916-.883.883z%27/%3E%3Cpath fill=%27%23FFAC33%27 d=%27m27.897 10.219 1.542.571.6 2.2a.667.667 0 0 0 1.287 0l.6-2.2 1.542-.571a.665.665 0 0 0 0-1.25l-1.534-.568-.605-2.415a.667.667 0 0 0-1.293 0l-.605 2.415-1.534.568a.665.665 0 0 0 0 1.25m-16.936 9.628 2.61.966.966 2.61a1.103 1.103 0 0 0 2.07 0l.966-2.61 2.609-.966a1.103 1.103 0 0 0 0-2.07l-2.609-.966-.966-2.61a1.105 1.105 0 0 0-2.07 0l-.966 2.61-2.61.966a1.104 1.104 0 0 0 0 2.07M23.13 4.36l1.383.512.512 1.382a.585.585 0 0 0 1.096 0l.512-1.382 1.382-.512a.584.584 0 0 0 0-1.096l-1.382-.512-.512-1.382a.585.585 0 0 0-1.096 0l-.512 1.382-1.383.512a.585.585 0 0 0 0 1.096%27/%3E%3C/g%3E%3C/svg%3E")
|
||||
0 0,
|
||||
/* magic wand easter egg */
|
||||
cursor: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='40' height='48' viewport='0 0 100 100' style='fill:black;font-size:24px;'><text y='50%'>🪄</text></svg>")
|
||||
16 0,
|
||||
auto;
|
||||
}
|
||||
|
||||
|
@@ -206,14 +206,15 @@ const Previously = () => (
|
||||
font-family: "Comic Neue", "Comic Sans MS", "Comic Sans", "Inter", sans-serif;
|
||||
font-weight: 600 !important;
|
||||
}
|
||||
header nav a span {
|
||||
font-size: 1.1em !important;
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
header nav > a span:nth-of-type(2) {
|
||||
header nav > div:first-of-type span:last-of-type {
|
||||
font-size: 1.4em !important;
|
||||
font-weight: 700 !important;
|
||||
}
|
||||
header nav div:last-of-type a span {
|
||||
font-size: 1.1em !important;
|
||||
font-weight: 700 !important;
|
||||
line-height: 1.1;
|
||||
}
|
||||
main > div > div {
|
||||
font-size: 1.1em !important;
|
||||
text-align: center;
|
||||
|
19
yarn.lock
19
yarn.lock
@@ -2539,9 +2539,9 @@ eastasianwidth@^0.2.0:
|
||||
integrity sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==
|
||||
|
||||
electron-to-chromium@^1.4.17:
|
||||
version "1.4.43"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.43.tgz#665c0cd8d5e7cce0ba78d90a514c8c813ca3bdbe"
|
||||
integrity sha512-PO3kEfcxPrti/4STbXvCkNIF4fgWvCKl2508e6UI7KomCDffpIfeBZLXsh5DK/XGsjUw3kwq6WEsi0MJTlGAdg==
|
||||
version "1.4.44"
|
||||
resolved "https://registry.yarnpkg.com/electron-to-chromium/-/electron-to-chromium-1.4.44.tgz#8a41923afdd6ef5ddabe001626036ba5d1d64ae6"
|
||||
integrity sha512-tHGWiUUmY7GABK8+DNcr474cnZDTzD8x1736SlDosVH8+/vRJeqfaIBAEHFtMjddz/0T4rKKYsxEc8BwQRdBpw==
|
||||
|
||||
email-regex@^5.0.0:
|
||||
version "5.0.0"
|
||||
@@ -3574,11 +3574,6 @@ hastscript@^7.0.0:
|
||||
property-information "^6.0.0"
|
||||
space-separated-tokens "^2.0.0"
|
||||
|
||||
hex-rgb@^5.0.0:
|
||||
version "5.0.0"
|
||||
resolved "https://registry.yarnpkg.com/hex-rgb/-/hex-rgb-5.0.0.tgz#e2c9eb6a37498d66c5a350a221ed4c2c7d1a92d6"
|
||||
integrity sha512-NQO+lgVUCtHxZ792FodgW0zflK+ozS9X9dwGp9XvvmPlH7pyxd588cn24TD3rmPm/N0AIRXF10Otah8yKqGw4w==
|
||||
|
||||
hoist-non-react-statics@^3.3.0:
|
||||
version "3.3.2"
|
||||
resolved "https://registry.yarnpkg.com/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz#ece0acaf71d62c2969c2ec59feff42a4b1a85b45"
|
||||
@@ -4228,10 +4223,10 @@ markdown-table@^3.0.0:
|
||||
resolved "https://registry.yarnpkg.com/markdown-table/-/markdown-table-3.0.2.tgz#9b59eb2c1b22fe71954a65ff512887065a7bb57c"
|
||||
integrity sha512-y8j3a5/DkJCmS5x4dMCQL+OR0+2EAq3DOtio1COSHsmW2BGXnNCK3v12hJt1LrUz5iZH5g0LmuYOjDdI+czghA==
|
||||
|
||||
marked@^4.0.9:
|
||||
version "4.0.9"
|
||||
resolved "https://registry.yarnpkg.com/marked/-/marked-4.0.9.tgz#96862d67bd31b4770917f01c93e1d9b3cc73ed96"
|
||||
integrity sha512-HmoFvQwFLxNESeGupeOC+6CLb5WzcCWQmqvVetsErmrI3vrZ6gBumty5IP0ynLPR0zYSoVY7ITC1GffsYIGkog==
|
||||
marked@^4.0.10:
|
||||
version "4.0.10"
|
||||
resolved "https://registry.yarnpkg.com/marked/-/marked-4.0.10.tgz#423e295385cc0c3a70fa495e0df68b007b879423"
|
||||
integrity sha512-+QvuFj0nGgO970fySghXGmuw+Fd0gD2x3+MqCWLIPf5oxdv1Ka6b2q+z9RP01P/IaKPMEramy+7cNy/Lw8c3hw==
|
||||
|
||||
mathml-tag-names@^2.1.3:
|
||||
version "2.1.3"
|
||||
|
Reference in New Issue
Block a user