1
mirror of https://github.com/jakejarvis/jarv.is.git synced 2025-07-21 15:01:18 -04:00

properly merge multiple class names

This commit is contained in:
2022-01-20 10:38:18 -05:00
parent 0462428a54
commit 2162e9d563
10 changed files with 47 additions and 34 deletions

View File

@@ -1,6 +1,7 @@
/* all code */ /* all code */
.code { .code {
font-size: 0.925em; font-size: 0.925em;
tab-size: 2;
page-break-inside: avoid; page-break-inside: avoid;
border: 1px solid var(--kinda-light); border: 1px solid var(--kinda-light);
} }
@@ -16,13 +17,26 @@
margin: 1em auto; margin: 1em auto;
} }
.block .copy_btn {
position: absolute;
top: 0;
right: 0;
padding: 0.65em;
color: var(--medium-dark);
background-color: var(--background-inner);
border: 1px solid var(--kinda-light);
}
.block .copy_btn:hover {
color: var(--link);
}
/* the following sub-classes MUST be global -- the highlight rehype plugin isn't aware of this file */ /* the following sub-classes MUST be global -- the highlight rehype plugin isn't aware of this file */
.block :global(.code-highlight) { .block :global(.code-highlight) {
display: block; display: block;
overflow-x: auto; overflow-x: auto;
padding: 1em; padding: 1em;
tab-size: 2;
color: var(--code-text); color: var(--code-text);
background-color: var(--code-background); background-color: var(--code-background);
} }

View File

@@ -1,3 +1,4 @@
import classNames from "classnames";
import CopyButton from "../CopyButton/CopyButton"; import CopyButton from "../CopyButton/CopyButton";
import type { ReactNode } from "react"; import type { ReactNode } from "react";
@@ -13,15 +14,15 @@ const CodeBlock = (props: Props) => {
// full multi-line code blocks with prism highlighting and copy-to-clipboard button // full multi-line code blocks with prism highlighting and copy-to-clipboard button
return ( return (
<div className={styles.block}> <div className={styles.block}>
<CopyButton source={props.children} /> <CopyButton source={props.children} className={styles.copy_btn} />
<code {...props} className={`${styles.code} ${props.className}`}> <code {...props} className={classNames(styles.code, props.className)}>
{props.children} {props.children}
</code> </code>
</div> </div>
); );
} else { } else {
// inline code in paragraphs, headings, etc. (not highlighted) // inline code in paragraphs, headings, etc. (not highlighted)
return <code className={`${styles.code} ${styles.inline}`}>{props.children}</code>; return <code className={classNames(styles.code, styles.inline)}>{props.children}</code>;
} }
}; };

View File

@@ -160,7 +160,7 @@ const ContactForm = () => {
<span>Sending...</span> <span>Sending...</span>
) : ( ) : (
<> <>
<SendIcon className={`icon ${styles.send_icon}`} /> <span>Send</span> <SendIcon className={classNames("icon", styles.send_icon)} /> <span>Send</span>
</> </>
)} )}
</button> </button>

View File

@@ -1,19 +1,8 @@
.copy { .copy {
position: absolute;
top: 0;
right: 0;
padding: 0.65em;
line-height: 1; line-height: 1;
color: var(--medium-dark);
background-color: var(--background-inner);
border: 1px solid var(--kinda-light);
cursor: pointer; cursor: pointer;
} }
.copy:hover {
color: var(--link);
}
.success { .success {
color: var(--success) !important; color: var(--success) !important;
} }

View File

@@ -11,9 +11,10 @@ const cx = classNames.bind(styles);
type Props = { type Props = {
source: ReactNode; source: ReactNode;
timeout?: number; timeout?: number;
className?: string;
}; };
const CopyButton = ({ source, timeout = 2000 }: Props) => { const CopyButton = ({ source, timeout = 2000, className }: Props) => {
const [copied, setCopied] = useState(false); const [copied, setCopied] = useState(false);
const handleCopy = (e) => { const handleCopy = (e) => {
@@ -43,7 +44,7 @@ const CopyButton = ({ source, timeout = 2000 }: Props) => {
return ( return (
<button <button
className={cx({ copy: true, success: !!copied })} className={cx({ copy: true, success: !!copied }, className)}
title="Copy to clipboard" title="Copy to clipboard"
aria-label="Copy to clipboard" aria-label="Copy to clipboard"
onClick={handleCopy} onClick={handleCopy}

View File

@@ -1,3 +1,4 @@
import classNames from "classnames";
import type { HTMLAttributes } from "react"; import type { HTMLAttributes } from "react";
import styles from "./Heading.module.css"; import styles from "./Heading.module.css";
@@ -8,7 +9,7 @@ type Props = HTMLAttributes<HTMLHeadingElement> & {
const Heading = ({ as: Component, children, ...rest }: Props) => { const Heading = ({ as: Component, children, ...rest }: Props) => {
return ( return (
<Component className={`${styles.heading} ${styles[Component] || ""}`} {...rest}> <Component className={classNames(styles.heading, styles[Component])} {...rest}>
{children} {children}
</Component> </Component>
); );

View File

@@ -1,5 +1,6 @@
import { memo } from "react"; import { memo } from "react";
import Link from "next/link"; import Link from "next/link";
import classNames from "classnames";
import ThemeToggle from "../ThemeToggle/ThemeToggle"; import ThemeToggle from "../ThemeToggle/ThemeToggle";
import { HomeIcon, NotesIcon, ProjectsIcon, ContactIcon } from "../Icons"; import { HomeIcon, NotesIcon, ProjectsIcon, ContactIcon } from "../Icons";
@@ -7,22 +8,22 @@ import styles from "./Menu.module.css";
const links = [ const links = [
{ {
icon: <HomeIcon className={`icon ${styles.icon}`} />, icon: <HomeIcon className={classNames("icon", styles.icon)} />,
text: "Home", text: "Home",
href: "/", href: "/",
}, },
{ {
icon: <NotesIcon className={`icon ${styles.icon}`} />, icon: <NotesIcon className={classNames("icon", styles.icon)} />,
text: "Notes", text: "Notes",
href: "/notes/", href: "/notes/",
}, },
{ {
icon: <ProjectsIcon className={`icon ${styles.icon}`} />, icon: <ProjectsIcon className={classNames("icon", styles.icon)} />,
text: "Projects", text: "Projects",
href: "/projects/", href: "/projects/",
}, },
{ {
icon: <ContactIcon className={`icon ${styles.icon}`} />, icon: <ContactIcon className={classNames("icon", styles.icon)} />,
text: "Contact", text: "Contact",
href: "/contact/", href: "/contact/",
}, },

View File

@@ -1,4 +1,5 @@
import Link from "next/link"; import Link from "next/link";
import classNames from "classnames";
import { format } from "date-fns"; import { format } from "date-fns";
import HitCounter from "../HitCounter/HitCounter"; import HitCounter from "../HitCounter/HitCounter";
import { DateIcon, TagIcon, EditIcon, ViewsIcon } from "../Icons"; import { DateIcon, TagIcon, EditIcon, ViewsIcon } from "../Icons";
@@ -13,7 +14,7 @@ const NoteMeta = ({ slug, date, title, tags = [] }: Props) => (
<div className={styles.meta}> <div className={styles.meta}>
<div className={styles.date}> <div className={styles.date}>
<span> <span>
<DateIcon className={`icon ${styles.icon}`} /> <DateIcon className={classNames("icon", styles.icon)} />
</span> </span>
<span title={format(new Date(date), "PPppp")}> <span title={format(new Date(date), "PPppp")}>
<Link href={`/notes/${slug}/`}> <Link href={`/notes/${slug}/`}>
@@ -25,7 +26,7 @@ const NoteMeta = ({ slug, date, title, tags = [] }: Props) => (
{tags.length > 0 && ( {tags.length > 0 && (
<div className={styles.tags}> <div className={styles.tags}>
<span> <span>
<TagIcon className={`icon ${styles.icon}`} /> <TagIcon className={classNames("icon", styles.icon)} />
</span> </span>
{tags.map((tag) => ( {tags.map((tag) => (
<span key={tag} className={styles.tag}> <span key={tag} className={styles.tag}>
@@ -37,7 +38,7 @@ const NoteMeta = ({ slug, date, title, tags = [] }: Props) => (
<div> <div>
<span> <span>
<EditIcon className={`icon ${styles.icon}`} /> <EditIcon className={classNames("icon", styles.icon)} />
</span> </span>
<span> <span>
<a <a
@@ -53,7 +54,7 @@ const NoteMeta = ({ slug, date, title, tags = [] }: Props) => (
<div> <div>
<span> <span>
<ViewsIcon className={`icon ${styles.icon}`} /> <ViewsIcon className={classNames("icon", styles.icon)} />
</span> </span>
<HitCounter slug={`notes/${slug}`} /> <HitCounter slug={`notes/${slug}`} />
</div> </div>

View File

@@ -1,10 +1,15 @@
import { useEffect, useState, memo } from "react"; import { useEffect, useState, memo } from "react";
import { useTheme } from "next-themes"; import { useTheme } from "next-themes";
import classNames from "classnames";
import styles from "./ThemeToggle.module.css"; import styles from "./ThemeToggle.module.css";
type Props = {
className?: string;
};
// modified from Twemoji lightbulb: // modified from Twemoji lightbulb:
const BulbIcon = ({ on = false, className = "" }) => ( const BulbIcon = ({ on = false, className }) => (
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36" className={className}> <svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 36 36" className={className}>
<g fill="none"> <g fill="none">
<path <path
@@ -31,13 +36,13 @@ const BulbIcon = ({ on = false, className = "" }) => (
</svg> </svg>
); );
const ThemeToggle = ({ className = "" }) => { const ThemeToggle = ({ className }: Props) => {
const [mounted, setMounted] = useState(false); const [mounted, setMounted] = useState(false);
const { resolvedTheme, setTheme } = useTheme(); const { resolvedTheme, setTheme } = useTheme();
// render a dummy bulb until we're fully mounted and self-aware // render a dummy bulb until we're fully mounted and self-aware
useEffect(() => setMounted(true), []); useEffect(() => setMounted(true), []);
if (!mounted) return <BulbIcon on={false} className={`icon ${className}`} />; if (!mounted) return <BulbIcon on={false} className={classNames("icon", className)} />;
return ( return (
<button <button
@@ -46,7 +51,7 @@ const ThemeToggle = ({ className = "" }) => {
title={resolvedTheme === "light" ? "Toggle Dark Mode" : "Toggle Light Mode"} title={resolvedTheme === "light" ? "Toggle Dark Mode" : "Toggle Light Mode"}
aria-hidden={true} aria-hidden={true}
> >
<BulbIcon on={resolvedTheme === "light"} className={`icon ${className}`} /> <BulbIcon on={resolvedTheme === "light"} className={classNames("icon", className)} />
</button> </button>
); );
}; };

View File

@@ -27,9 +27,9 @@ const Video = ({ webm, mp4, thumbnail, subs, autoplay }: Props) => {
attributes: { attributes: {
controlsList: "nodownload", controlsList: "nodownload",
preload: "metadata", preload: "metadata",
autoPlay: autoplay, autoPlay: !!autoplay,
muted: autoplay, muted: !!autoplay,
loop: autoplay, loop: !!autoplay,
}, },
}, },
}; };