1
mirror of https://github.com/jakejarvis/jarv.is.git synced 2025-04-26 15:28:28 -04:00
jarv.is/components/Heading/Heading.tsx

79 lines
2.4 KiB
TypeScript

import innerText from "react-innertext";
import HeadingAnchor from "../HeadingAnchor";
import { styled, theme } from "../../lib/styles/stitches.config";
import type { ComponentProps } from "react";
const Anchor = styled(HeadingAnchor, {
margin: "0 0.4em",
padding: "0 0.2em",
color: theme.colors.medium,
opacity: 0, // overridden on hover below (except on small screens)
"&:hover, &:focus-visible": {
color: theme.colors.link,
},
"@medium": {
margin: "0 0.2em",
padding: "0 0.4em",
// don't require hover to show anchor link on small (likely touch) screens
opacity: 1,
},
});
const H = styled("h1", {
marginTop: "1em",
marginBottom: "0.5em",
lineHeight: 1.5,
// offset (approximately) with sticky header so jumped-to content isn't hiding behind it.
// note: use rem so it isn't based on the heading's font size.
scrollMarginTop: "5.5rem",
"@medium": {
scrollMarginTop: "6.5rem",
},
// show anchor link when hovering anywhere over the heading line, or on keyboard tab focus
[`&:hover ${Anchor}, ${Anchor}:focus-visible`]: {
opacity: 1,
},
variants: {
// subtle horizontal rule under the heading, set by default on `<h2>`s
divider: {
true: {
paddingBottom: "0.25em",
borderBottom: `1px solid ${theme.colors.kindaLight}`,
},
false: {},
},
},
});
export type HeadingProps = ComponentProps<typeof H> & {
level: 1 | 2 | 3 | 4 | 5 | 6;
divider?: boolean;
};
const Heading = ({ level, id, divider, children, ...rest }: HeadingProps) => {
return (
<H as={`h${level}`} id={id} divider={divider || level === 2} {...rest}>
{children}
{/* add anchor link to H2s and H3s. ID is either provided or automatically generated by rehype-slug. */}
{id && (level === 2 || level === 3) && <Anchor id={id} title={innerText(children)} />}
</H>
);
};
export const H1 = (props: Omit<HeadingProps, "level">) => <Heading level={1} {...props} />;
export const H2 = (props: Omit<HeadingProps, "level">) => <Heading level={2} {...props} />;
export const H3 = (props: Omit<HeadingProps, "level">) => <Heading level={3} {...props} />;
export const H4 = (props: Omit<HeadingProps, "level">) => <Heading level={4} {...props} />;
export const H5 = (props: Omit<HeadingProps, "level">) => <Heading level={5} {...props} />;
export const H6 = (props: Omit<HeadingProps, "level">) => <Heading level={6} {...props} />;
export default Heading;