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

make sticky header optional via prop

This commit is contained in:
2022-02-16 10:30:18 -05:00
parent b6c018875f
commit 17104d765b
14 changed files with 116 additions and 120 deletions

View File

@ -1,8 +1,7 @@
import classNames from "classnames/bind";
import classNames from "classnames";
import CopyButton from "../CopyButton/CopyButton";
import styles from "./CodeBlock.module.css";
const cx = classNames.bind(styles);
type CodeBlockProps = JSX.IntrinsicElements["code"] & {
forceBlock?: boolean;
@ -19,7 +18,11 @@ const CodeBlock = ({ forceBlock, className, children, ...rest }: CodeBlockProps)
<div className={styles.block}>
<CopyButton source={children} className={styles.copy_btn} />
<code
className={cx(styles.code, { highlight: prismEnabled }, className?.replace("code-highlight", "").trim())}
className={classNames(
styles.code,
prismEnabled && styles.highlight,
className?.replace("code-highlight", "").trim()
)}
{...rest}
>
{children}

View File

@ -1,5 +1,5 @@
import { useState } from "react";
import classNames from "classnames/bind";
import classNames from "classnames";
import { Formik, Form, Field } from "formik";
import TextareaAutosize from "react-textarea-autosize";
import Link from "../Link/Link";
@ -8,7 +8,6 @@ import { SendIcon, CheckOcticon, XOcticon } from "../Icons";
import type { FormikHelpers } from "formik";
import styles from "./ContactForm.module.css";
const cx = classNames.bind(styles);
type Values = {
name: string;
@ -103,7 +102,7 @@ const ContactForm = ({ className }: ContactFormProps) => {
{({ field, meta }) => (
<input
type="text"
className={cx(styles.input, { missing: meta.error && meta.touched })}
className={classNames(styles.input, meta.error && meta.touched && styles.missing)}
placeholder="Name"
disabled={success}
{...field}
@ -116,7 +115,7 @@ const ContactForm = ({ className }: ContactFormProps) => {
<input
type="email"
inputMode="email"
className={cx(styles.input, { missing: meta.error && meta.touched })}
className={classNames(styles.input, meta.error && meta.touched && styles.missing)}
placeholder="Email"
disabled={success}
{...field}
@ -127,7 +126,7 @@ const ContactForm = ({ className }: ContactFormProps) => {
<Field name="message">
{({ field, meta }) => (
<TextareaAutosize
className={cx(styles.input, styles.textarea, { missing: meta.error && meta.touched })}
className={classNames(styles.input, styles.textarea, meta.error && meta.touched && styles.missing)}
placeholder="Write something..."
minRows={5}
disabled={success}
@ -154,7 +153,7 @@ const ContactForm = ({ className }: ContactFormProps) => {
<div className={styles.action_row}>
<button
className={cx(styles.btn_submit, { hidden: success })}
className={classNames(styles.btn_submit, success && styles.hidden)}
type="submit"
title="Send Message"
aria-label="Send Message"
@ -171,11 +170,11 @@ const ContactForm = ({ className }: ContactFormProps) => {
</button>
<span
className={cx({
result_success: success,
result_error: !success,
hidden: !submitted || !feedback || isSubmitting,
})}
className={classNames(
success && styles.result_success,
!success && styles.result_error,
(!submitted || !feedback || isSubmitting) && styles.hidden
)}
>
{success ? (
<CheckOcticon className={styles.result_icon} fill="CurrentColor" />

View File

@ -1,12 +1,11 @@
import { forwardRef, useState, useEffect } from "react";
import classNames from "classnames/bind";
import classNames from "classnames";
import copy from "copy-to-clipboard";
import innerText from "react-innertext";
import { ClipboardOcticon, CheckOcticon } from "../Icons";
import type { ReactNode, Ref } from "react";
import styles from "./CopyButton.module.css";
const cx = classNames.bind(styles);
type CopyButtonProps = {
source: ReactNode;
@ -47,7 +46,7 @@ const CopyButton = forwardRef(function CopyButton(
return (
<button
className={cx(styles.button, { success: !!copied }, className)}
className={classNames(styles.button, copied && styles.success, className)}
title="Copy to clipboard"
aria-label="Copy to clipboard"
onClick={handleCopy}

View File

@ -6,7 +6,7 @@ import * as config from "../../lib/config";
import styles from "./Footer.module.css";
type FooterProps = JSX.IntrinsicElements["div"];
type FooterProps = JSX.IntrinsicElements["footer"];
const Footer = ({ className, ...rest }: FooterProps) => (
<footer className={classNames(styles.footer, className)} {...rest}>

View File

@ -1,18 +1,21 @@
.header {
position: sticky;
top: 0;
width: 100%;
height: 4.5em;
padding: 0.7em 1.5em;
border-bottom: 1px solid var(--kinda-light);
background-color: var(--background-header);
backdrop-filter: saturate(180%) blur(5px);
z-index: 1000;
/* light-dark theme switch fading */
transition: color 0.25s ease, background 0.25s ease, border 0.25s ease;
}
.sticky {
position: sticky;
top: 0;
backdrop-filter: saturate(180%) blur(5px);
z-index: 1000;
}
.nav {
display: flex;
align-items: center;

View File

@ -5,10 +5,12 @@ import Menu from "../Menu/Menu";
import styles from "./Header.module.css";
type HeaderProps = JSX.IntrinsicElements["div"];
type HeaderProps = JSX.IntrinsicElements["header"] & {
sticky?: boolean;
};
const Header = ({ className }: HeaderProps) => (
<header className={classNames(styles.header, className)}>
const Header = ({ sticky, className, ...rest }: HeaderProps) => (
<header className={classNames(styles.header, sticky && styles.sticky, className)} {...rest}>
<nav className={styles.nav}>
<Selfie className={styles.selfie} />
<Menu className={styles.menu} />

View File

@ -9,10 +9,11 @@ import themes, { toCSS } from "../../lib/config/themes";
import styles from "./Layout.module.css";
type LayoutProps = JSX.IntrinsicElements["div"] & {
noContainer?: boolean; // pass true to disable default `<main>` container styles with padding, etc.
container?: boolean; // pass false to disable default `<main>` container styles with padding, etc.
stickyHeader?: boolean; // pass false to override default stickiness of header when scrolling
};
const Layout = ({ noContainer, className, children, ...rest }: LayoutProps) => {
const Layout = ({ container = true, stickyHeader = true, className, children, ...rest }: LayoutProps) => {
const { resolvedTheme } = useTheme();
return (
@ -38,15 +39,15 @@ const Layout = ({ noContainer, className, children, ...rest }: LayoutProps) => {
</Script>
<div className={classNames(styles.flex, className)} {...rest}>
<Header />
<Header sticky={stickyHeader} />
{/* passing `noContainer={true}` to Layout allows 100% control of the content area on a per-page basis */}
{noContainer ? (
<>{children}</>
) : (
{/* passing `container={false}` to Layout allows 100% control of the content area on a per-page basis */}
{container ? (
<main className={styles.default}>
<div className={styles.container}>{children}</div>
</main>
) : (
<>{children}</>
)}
<Footer className={styles.footer} />

View File

@ -1,8 +1,7 @@
import Link from "next/link";
import classNames from "classnames/bind";
import classNames from "classnames";
import styles from "./MenuLink.module.css";
const cx = classNames.bind(styles);
export type MenuLinkProps = {
href?: string;
@ -20,7 +19,7 @@ const MenuLink = ({ icon: Icon, href, text, current, className }: MenuLinkProps)
if (href) {
return (
<Link href={href} prefetch={false}>
<a className={cx(styles.link, { current: !!current }, className)}>
<a className={classNames(styles.link, current && styles.current, className)}>
<Icon className={styles.icon} /> <span className={styles.label}>{text}</span>
</a>
</Link>

View File

@ -4,17 +4,17 @@ import type { NoteMetaType } from "../../types";
import styles from "./NoteTitle.module.css";
type NoteTitleProps = Pick<NoteMetaType, "slug" | "htmlTitle"> & JSX.IntrinsicElements["a"];
type NoteTitleProps = Pick<NoteMetaType, "slug" | "htmlTitle"> & JSX.IntrinsicElements["h1"];
const NoteTitle = ({ slug, htmlTitle, className, ...rest }: NoteTitleProps) => (
<h1 className={classNames(styles.title, className)}>
<h1 className={classNames(styles.title, className)} {...rest}>
<Link
href={{
pathname: "/notes/[slug]/",
query: { slug: slug },
}}
>
<a className={styles.link} dangerouslySetInnerHTML={{ __html: htmlTitle }} {...rest} />
<a className={styles.link} dangerouslySetInnerHTML={{ __html: htmlTitle }} />
</Link>
</h1>
);

View File

@ -1,10 +1,9 @@
import { useEffect, useRef } from "react";
import classNames from "classnames/bind";
import classNames from "classnames";
import styles from "./Wallpaper.module.css";
const cx = classNames.bind(styles);
type WallpaperProps = JSX.IntrinsicElements["div"] & {
type WallpaperProps = JSX.IntrinsicElements["main"] & {
image: string;
tile?: boolean;
};
@ -16,7 +15,7 @@ const Wallpaper = ({ image, tile, className, ...rest }: WallpaperProps) => {
bgRef.current.style.backgroundImage = `url(${image})`;
}, []); // eslint-disable-line react-hooks/exhaustive-deps
return <main ref={bgRef} className={cx(styles.wallpaper, { tile: !!tile }, className)} {...rest} />;
return <main ref={bgRef} className={classNames(styles.wallpaper, tile && styles.tile, className)} {...rest} />;
};
export default Wallpaper;