client: improve markdown rendering

This commit is contained in:
Max Leiter 2022-03-23 16:28:39 -07:00
parent 60f2ab99b3
commit c55ca681b4
No known key found for this signature in database
GPG key ID: A3512F2F2F17EBDA
15 changed files with 285 additions and 259 deletions

View file

@ -1,41 +1,48 @@
.card {
max-width: var(--main-content);
margin: var(--gap) auto;
padding: 2;
border: 1px solid var(--light-gray);
}
.input { .input {
background: #efefef; background: #efefef;
} }
.descriptionContainer { .descriptionContainer {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-height: 400px; min-height: 400px;
overflow: auto; overflow: auto;
} }
.fileNameContainer { .fileNameContainer {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
height: 36px; height: 36px;
} }
.fileNameContainer { .fileNameContainer {
display: flex; display: flex;
align-content: center; align-content: center;
} }
.fileNameContainer > div { .fileNameContainer > div {
/* Override geist-ui styling */ /* Override geist-ui styling */
margin: 0 !important; margin: 0 !important;
} }
.textarea { .textarea {
height: 100%; height: 100%;
} }
.actionWrapper { .actionWrapper {
position: relative; position: relative;
z-index: 1; z-index: 1;
} }
.actionWrapper .actions { .actionWrapper .actions {
position: absolute; position: absolute;
right: 0; right: 0;
} }

View file

@ -34,9 +34,6 @@ const FormattingIcons = ({ textareaRef, setText }: { textareaRef?: RefObject<HTM
const newText = `${before}**${selectedText}**${after}` const newText = `${before}**${selectedText}**${after}`
setText(newText) setText(newText)
// TODO; fails because settext async
textareaRef.current.setSelectionRange(before.length + 2, before.length + 2 + selectedText.length)
} }
}, [setText, textareaRef]) }, [setText, textareaRef])
@ -50,8 +47,6 @@ const FormattingIcons = ({ textareaRef, setText }: { textareaRef?: RefObject<HTM
const selectedText = text.substring(selectionStart, selectionEnd) const selectedText = text.substring(selectionStart, selectionEnd)
const newText = `${before}*${selectedText}*${after}` const newText = `${before}*${selectedText}*${after}`
setText(newText) setText(newText)
textareaRef.current.focus()
textareaRef.current.setSelectionRange(before.length + 1, before.length + 1 + selectedText.length)
} }
}, [setText, textareaRef]) }, [setText, textareaRef])
@ -71,8 +66,6 @@ const FormattingIcons = ({ textareaRef, setText }: { textareaRef?: RefObject<HTM
} }
const newText = `${before}${formattedText}${after}` const newText = `${before}${formattedText}${after}`
setText(newText) setText(newText)
textareaRef.current.focus()
textareaRef.current.setSelectionRange(before.length + 1, before.length + 1 + selectedText.length)
} }
}, [setText, textareaRef]) }, [setText, textareaRef])
@ -92,8 +85,6 @@ const FormattingIcons = ({ textareaRef, setText }: { textareaRef?: RefObject<HTM
} }
const newText = `${before}${formattedText}${after}` const newText = `${before}${formattedText}${after}`
setText(newText) setText(newText)
textareaRef.current.focus()
textareaRef.current.setSelectionRange(before.length + 1, before.length + 1 + selectedText.length)
} }
}, [setText, textareaRef]) }, [setText, textareaRef])

View file

@ -23,34 +23,6 @@ type Props = {
remove?: () => void remove?: () => void
} }
const DownloadButton = ({ rawLink }: { rawLink?: string }) => {
return (<div className={styles.actionWrapper}>
<ButtonGroup className={styles.actions}>
<Tooltip text="Download">
<a href={`${rawLink}?download=true`} target="_blank" rel="noopener noreferrer">
<Button
scale={2 / 3} px={0.6}
icon={<Download />}
auto
aria-label="Download"
/>
</a>
</Tooltip>
<Tooltip text="Open raw in new tab">
<a href={rawLink} target="_blank" rel="noopener noreferrer">
<Button
scale={2 / 3} px={0.6}
icon={<ExternalLink />}
auto
aria-label="Open raw file in new tab"
/>
</a>
</Tooltip>
</ButtonGroup>
</div>)
}
const Document = ({ remove, title, content, setTitle, setContent, initialTab = 'edit', skeleton, handleOnContentChange }: Props) => { const Document = ({ remove, title, content, setTitle, setContent, initialTab = 'edit', skeleton, handleOnContentChange }: Props) => {
const codeEditorRef = useRef<HTMLTextAreaElement>(null) const codeEditorRef = useRef<HTMLTextAreaElement>(null)
const [tab, setTab] = useState(initialTab) const [tab, setTab] = useState(initialTab)
@ -98,7 +70,7 @@ const Document = ({ remove, title, content, setTitle, setContent, initialTab = '
return ( return (
<> <>
<Spacer height={1} /> <Spacer height={1} />
<Card marginBottom={'var(--gap)'} marginTop={'var(--gap)'} style={{ maxWidth: 'var(--main-content)', margin: "0 auto" }}> <div className={styles.card}>
<div className={styles.fileNameContainer}> <div className={styles.fileNameContainer}>
<Input <Input
placeholder="MyFile.md" placeholder="MyFile.md"
@ -138,8 +110,7 @@ const Document = ({ remove, title, content, setTitle, setContent, initialTab = '
</Tabs> </Tabs>
</div > </div >
</Card > </div >
<Spacer height={1} />
</> </>
) )
} }

View file

@ -47,7 +47,7 @@ const ListItem = ({ post }: { post: any }) => {
<Divider h="1px" my={0} /> <Divider h="1px" my={0} />
<Card.Content > <Card.Content>
{post.files.map((file: any) => { {post.files.map((file: any) => {
return <FilenameInput key={file.id} title={file.title} /> return <FilenameInput key={file.id} title={file.title} />
})} })}

View file

@ -49,7 +49,7 @@ const MarkdownPreview = ({ height = 500, fileId, content, title }: Props) => {
fetchPost() fetchPost()
}, [content, fileId, title]) }, [content, fileId, title])
return (<> return (<>
{isLoading ? <div>Loading...</div> : <article data-theme={theme} className={styles.markdownPreview} dangerouslySetInnerHTML={{ __html: preview }} style={{ {isLoading ? <div>Loading...</div> : <article className={styles.markdownPreview} dangerouslySetInnerHTML={{ __html: preview }} style={{
height height
}} />} }} />}
</>) </>)

View file

@ -1,12 +1,12 @@
.markdownPreview pre { .markdownPreview pre {
border-radius: 3px; border-radius: 3px;
font-family: "Courier New", Courier, monospace; font-family: "Courier New", Courier, monospace;
font-size: 14px; font-size: 14px;
line-height: 1.42857143; line-height: 1.42857143;
margin: 0; margin: 0;
padding: 10px; padding: 10px;
white-space: pre-wrap; white-space: pre-wrap;
word-wrap: break-word; word-wrap: break-word;
} }
.markdownPreview h1, .markdownPreview h1,
@ -15,8 +15,8 @@
.markdownPreview h4, .markdownPreview h4,
.markdownPreview h5, .markdownPreview h5,
.markdownPreview h6 { .markdownPreview h6 {
margin-top: 0; margin-top: 0;
margin-bottom: 0.5rem; margin-bottom: 0.5rem;
} }
/* Auto-linked headers */ /* Auto-linked headers */
@ -26,7 +26,7 @@
.markdownPreview h4 a, .markdownPreview h4 a,
.markdownPreview h5 a, .markdownPreview h5 a,
.markdownPreview h6 a { .markdownPreview h6 a {
color: inherit; color: inherit;
} }
/* Auto-linked headers */ /* Auto-linked headers */
@ -36,53 +36,58 @@
.markdownPreview h4 a:hover::after, .markdownPreview h4 a:hover::after,
.markdownPreview h5 a:hover::after, .markdownPreview h5 a:hover::after,
.markdownPreview h6 a:hover::after { .markdownPreview h6 a:hover::after {
content: "#"; content: "#";
font-size: 0.7em; font-size: 0.7em;
margin-left: 0.25em; margin-left: 0.25em;
font-weight: normal; font-weight: normal;
filter: opacity(0.5); filter: opacity(0.5);
} }
.markdownPreview h1 { .markdownPreview h1 {
font-size: 2rem; font-size: 2rem;
} }
.markdownPreview h2 { .markdownPreview h2 {
font-size: 1.5rem; font-size: 1.5rem;
} }
.markdownPreview h3 { .markdownPreview h3 {
font-size: 1.25rem; font-size: 1.25rem;
} }
.markdownPreview h4 { .markdownPreview h4 {
font-size: 1rem; font-size: 1rem;
} }
.markdownPreview h5 { .markdownPreview h5 {
font-size: 0.875rem; font-size: 0.875rem;
} }
.markdownPreview h6 { .markdownPreview h6 {
font-size: 0.75rem; font-size: 0.75rem;
} }
.markdownPreview ul { .markdownPreview ul {
list-style: inside; list-style: inside;
} }
.markdownPreview ul li::before { .markdownPreview ul li::before {
content: ""; content: "";
} }
.markdownPreview code { .markdownPreview code {
border-radius: 3px; border-radius: 3px;
white-space: pre-wrap; white-space: pre-wrap;
word-wrap: break-word; word-wrap: break-word;
color: inherit !important; color: inherit !important;
} }
.markdownPreview code::before, .markdownPreview code::before,
.markdownPreview code::after { .markdownPreview code::after {
content: ""; content: "";
}
.markdownPreview img {
max-width: 100%;
max-height: 350px;
} }

View file

@ -1,41 +1,40 @@
.input { .card {
background: #efefef; margin: var(--gap) auto;
padding: var(--gap);
border: 1px solid var(--light-gray);
border-radius: var(--radius);
} }
.descriptionContainer { .descriptionContainer {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
min-height: 400px; min-height: 400px;
overflow: auto; overflow: auto;
} }
.fileNameContainer { .fileNameContainer {
display: flex; display: flex;
justify-content: space-between; justify-content: space-between;
align-items: center; align-items: center;
height: 36px; height: 36px;
} }
.fileNameContainer { .fileNameContainer {
display: flex; display: flex;
align-content: center; align-content: center;
} }
.fileNameContainer > div { .fileNameContainer > div {
/* Override geist-ui styling */ /* Override geist-ui styling */
margin: 0 !important; margin: 0 !important;
}
.textarea {
height: 100%;
} }
.actionWrapper { .actionWrapper {
position: relative; position: relative;
z-index: 1; z-index: 1;
} }
.actionWrapper .actions { .actionWrapper .actions {
position: absolute; position: absolute;
right: 0; right: 0;
} }

View file

@ -84,7 +84,7 @@ const Document = ({ content, title, initialTab = 'edit', skeleton, id }: Props)
return ( return (
<> <>
<Spacer height={1} /> <Spacer height={1} />
<Card marginBottom={'var(--gap)'} marginTop={'var(--gap)'} style={{ maxWidth: 980, margin: "0 auto" }}> <div className={styles.card}>
<div className={styles.fileNameContainer}> <div className={styles.fileNameContainer}>
<Input <Input
value={title} value={title}
@ -121,8 +121,7 @@ const Document = ({ content, title, initialTab = 'edit', skeleton, id }: Props)
</Tabs> </Tabs>
</div > </div >
</Card > </div >
<Spacer height={1} />
</> </>
) )
} }

View file

@ -0,0 +1,18 @@
// useDebounce.js
import { useState, useEffect } from 'react';
export default function useDebounce(value: any, delay: number) {
const [debouncedValue, setDebouncedValue] = useState(value);
useEffect(() => {
const handler = setTimeout(() => {
setDebouncedValue(value);
}, delay);
return () => {
clearTimeout(handler);
};
}, [value, delay]);
return debouncedValue;
}

View file

@ -1,7 +1,18 @@
import { marked } from 'marked' import { marked, Lexer } from 'marked'
import Highlight, { defaultProps, Language, } from 'prism-react-renderer' import Highlight, { defaultProps, Language, } from 'prism-react-renderer'
import { renderToStaticMarkup } from 'react-dom/server' import { renderToStaticMarkup } from 'react-dom/server'
// image sizes. DDoS Safe?
const imageSizeLink = /^!?\[((?:\[[^\[\]]*\]|\\[\[\]]?|`[^`]*`|[^\[\]\\])*?)\]\(\s*(<(?:\\[<>]?|[^\s<>\\])*>|(?:\\[()]?|\([^\s\x00-\x1f()\\]*\)|[^\s\x00-\x1f()\\])*?(?:\s+=(?:[\w%]+)?x(?:[\w%]+)?)?)(?:\s+("(?:\\"?|[^"\\])*"|'(?:\\'?|[^'\\])*'|\((?:\\\)?|[^)\\])*\)))?\s*\)/;
//@ts-ignore
Lexer.rules.inline.normal.link = imageSizeLink;
//@ts-ignore
Lexer.rules.inline.gfm.link = imageSizeLink;
//@ts-ignore
Lexer.rules.inline.breaks.link = imageSizeLink;
//@ts-ignore //@ts-ignore
delete defaultProps.theme delete defaultProps.theme
// import linkStyles from '../components/link/link.module.css' // import linkStyles from '../components/link/link.module.css'

View file

@ -19,6 +19,7 @@
"cookie": "^0.4.2", "cookie": "^0.4.2",
"dotenv": "^16.0.0", "dotenv": "^16.0.0",
"js-cookie": "^3.0.1", "js-cookie": "^3.0.1",
"lodash.debounce": "^4.0.8",
"marked": "^4.0.12", "marked": "^4.0.12",
"next": "^12.1.1-canary.15", "next": "^12.1.1-canary.15",
"postcss": "^8.4.12", "postcss": "^8.4.12",

View file

@ -18,7 +18,7 @@ function MyApp({ Component, pageProps }: AppProps) {
const skeletonHighlightColor = 'var(--lighter-gray)' const skeletonHighlightColor = 'var(--lighter-gray)'
return ( return (
<> <div data-theme={theme}>
<Head> <Head>
<meta charSet="utf-8" /> <meta charSet="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" /> <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no" />
@ -33,13 +33,13 @@ function MyApp({ Component, pageProps }: AppProps) {
<meta name="theme-color" content="#ffffff" /> <meta name="theme-color" content="#ffffff" />
<title>Drift</title> <title>Drift</title>
</Head> </Head>
<GeistProvider themeType={theme} > <GeistProvider themeType={theme}>
<SkeletonTheme baseColor={skeletonBaseColor} highlightColor={skeletonHighlightColor}> <SkeletonTheme baseColor={skeletonBaseColor} highlightColor={skeletonHighlightColor}>
<CssBaseline /> <CssBaseline />
<Component {...pageProps} /> <Component {...pageProps} />
</SkeletonTheme> </SkeletonTheme>
</GeistProvider> </GeistProvider>
</> </div>
) )
} }

View file

@ -3,122 +3,145 @@
@import "./inter.css"; @import "./inter.css";
:root { :root {
/* Spacing */ /* Spacing */
--gap-quarter: 0.25rem; --gap-quarter: 0.25rem;
--gap-half: 0.5rem; --gap-half: 0.5rem;
--gap: 1rem; --gap: 1rem;
--gap-double: 2rem; --gap-double: 2rem;
--small-gap: 4rem; --small-gap: 4rem;
--big-gap: 4rem; --big-gap: 4rem;
--main-content: 55rem; --main-content: 55rem;
--radius: 8px; --radius: 8px;
--inline-radius: 5px; --inline-radius: 5px;
/* Typography */ /* Typography */
--font-sans: "Inter", -apple-system, BlinkMacSystemFont, Segoe UI, Roboto, --font-sans: "Inter", -apple-system, BlinkMacSystemFont, Segoe UI, Roboto,
Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif; Oxygen, Ubuntu, Cantarell, Fira Sans, Droid Sans, Helvetica Neue, sans-serif;
--font-mono: ui-monospace, "SFMono-Regular", "Consolas", "Liberation Mono", --font-mono: ui-monospace, "SFMono-Regular", "Consolas", "Liberation Mono",
"Menlo", monospace; "Menlo", monospace;
/* Transitions */ /* Transitions */
--transition: 0.1s ease-in-out; --transition: 0.1s ease-in-out;
--transition-slow: 0.3s ease-in-out; --transition-slow: 0.3s ease-in-out;
--page-nav-height: 64px; --page-nav-height: 64px;
--token: #999; --token: #999;
--comment: #999; --comment: #999;
--keyword: #fff; --keyword: #fff;
--name: #fff; --name: #fff;
--highlight: #2e2e2e; --highlight: #2e2e2e;
/* Dark Mode Colors */
--bg: #131415;
--fg: #fafbfc;
--gray: #666;
--light-gray: #444;
--lighter-gray: #222;
--lightest-gray: #1a1a1a;
--article-color: #eaeaea;
--header-bg: rgba(19, 20, 21, 0.45);
--gray-alpha: rgba(255, 255, 255, 0.5);
--selection: rgba(255, 255, 255, 0.99);
} }
[data-theme="light"] { [data-theme="light"] {
--token: #666; --token: #666;
--comment: #999; --comment: #999;
--keyword: #000; --keyword: #000;
--name: #333; --name: #333;
--highlight: #eaeaea; --highlight: #eaeaea;
--bg: #fff;
--fg: #000;
--gray: #888;
--light-gray: #dedede;
--lighter-gray: #f5f5f5;
--lightest-gray: #fafafa;
--article-color: #212121;
--header-bg: rgba(255, 255, 255, 0.8);
--gray-alpha: rgba(19, 20, 21, 0.5);
--selection: rgba(0, 0, 0, 0.99);
} }
* { * {
box-sizing: border-box; box-sizing: border-box;
} }
::selection { ::selection {
text-shadow: none; text-shadow: none;
background: var(--selection); background: var(--selection);
} }
html, html,
body { body {
padding: 0; padding: 0;
margin: 0; margin: 0;
font-size: 15px; font-size: 15px;
text-rendering: optimizeLegibility; text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased; -webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale; -moz-osx-font-smoothing: grayscale;
} }
body { body {
min-height: 100vh; min-height: 100vh;
font-family: var(--font-sans); font-family: var(--font-sans);
display: flex; display: flex;
flex-direction: column; flex-direction: column;
} }
p, p,
li { li {
letter-spacing: -0.33px; letter-spacing: -0.33px;
font-size: 1.125rem; font-size: 1.125rem;
} }
blockquote { blockquote {
font-style: italic; font-style: italic;
margin: 0; margin: 0;
padding-left: 1rem; padding-left: 1rem;
border-left: 3px solid var(--light-gray); border-left: 3px solid var(--light-gray);
} }
a.reset { a.reset {
outline: none; outline: none;
text-decoration: none; text-decoration: none;
} }
pre, pre,
code { code {
font-family: var(--font-mono) !important; font-family: var(--font-mono) !important;
} }
kbd { kbd {
font-family: var(--font-sans); font-family: var(--font-sans);
font-size: 1rem; font-size: 1rem;
padding: 2px 7px; padding: 2px 7px;
font-weight: 600; font-weight: 600;
background: var(--lighter-gray); background: var(--lighter-gray);
border-radius: 5px; border-radius: 5px;
} }
@media print { @media print {
:root { :root {
--bg: #fff; --bg: #fff;
--fg: #000; --fg: #000;
--gray: #888; --gray: #888;
--light-gray: #dedede; --light-gray: #dedede;
--lighter-gray: #f5f5f5; --lighter-gray: #f5f5f5;
--lightest-gray: #fafafa; --lightest-gray: #fafafa;
--article-color: #212121; --article-color: #212121;
--header-bg: rgba(255, 255, 255, 0.8); --header-bg: rgba(255, 255, 255, 0.8);
--gray-alpha: rgba(19, 20, 21, 0.5); --gray-alpha: rgba(19, 20, 21, 0.5);
--selection: rgba(0, 0, 0, 0.99); --selection: rgba(0, 0, 0, 0.99);
--token: #666; --token: #666;
--comment: #999; --comment: #999;
--keyword: #000; --keyword: #000;
--name: #333; --name: #333;
--highlight: #eaeaea; --highlight: #eaeaea;
} }
* { * {
text-shadow: none !important; text-shadow: none !important;
} }
} }

View file

@ -1,129 +1,125 @@
article { article {
max-width: var(--main-content); max-width: var(--main-content);
margin: 0 auto; margin: 0 auto;
line-height: 1.9; line-height: 1.9;
} }
article > * + * { article > * + * {
margin-top: 2em; margin-top: 2em;
} }
article img { article img {
max-width: 100%; max-width: 100%;
/* width: var(--main-content); */ margin: auto;
width: auto;
margin: auto;
display: block;
border-radius: var(--radius);
} }
article [id]::before { article [id]::before {
content: ""; content: "";
display: block; display: block;
height: 70px; height: 70px;
margin-top: -70px; margin-top: -70px;
visibility: hidden; visibility: hidden;
} }
/* Lists */ /* Lists */
article ul { article ul {
padding: 0; padding: 0;
list-style-position: inside; list-style-position: inside;
list-style-type: circle; list-style-type: circle;
} }
article ol { article ol {
padding: 0; padding: 0;
list-style-position: inside; list-style-position: inside;
} }
article ul li.reset { article ul li.reset {
display: flex; display: flex;
align-items: flex-start; align-items: flex-start;
list-style-type: none; list-style-type: none;
margin-left: -0.5rem; margin-left: -0.5rem;
} }
article ul li.reset .check { article ul li.reset .check {
display: flex; display: flex;
align-items: center; align-items: center;
margin-right: 0.51rem; margin-right: 0.51rem;
} }
/* Checkbox */ /* Checkbox */
input[type="checkbox"] { input[type="checkbox"] {
vertical-align: middle; vertical-align: middle;
appearance: none; appearance: none;
display: inline-block; display: inline-block;
background-origin: border-box; background-origin: border-box;
user-select: none; user-select: none;
flex-shrink: 0; flex-shrink: 0;
height: 1rem; height: 1rem;
width: 1rem; width: 1rem;
background-color: var(--bg); background-color: var(--bg);
color: var(--fg); color: var(--fg);
border: 1px solid var(--fg); border: 1px solid var(--fg);
border-radius: 3px; border-radius: 3px;
} }
input[type="checkbox"]:checked { input[type="checkbox"]:checked {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='black' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M5.707 7.293a1 1 0 0 0-1.414 1.414l2 2a1 1 0 0 0 1.414 0l4-4a1 1 0 0 0-1.414-1.414L7 8.586 5.707 7.293z'/%3e%3c/svg%3e"); background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='black' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M5.707 7.293a1 1 0 0 0-1.414 1.414l2 2a1 1 0 0 0 1.414 0l4-4a1 1 0 0 0-1.414-1.414L7 8.586 5.707 7.293z'/%3e%3c/svg%3e");
border-color: transparent; border-color: transparent;
background-color: currentColor; background-color: currentColor;
background-size: 100% 100%; background-size: 100% 100%;
background-position: center; background-position: center;
background-repeat: no-repeat; background-repeat: no-repeat;
} }
html[data-theme="light"] input[type="checkbox"]:checked { html[data-theme="light"] input[type="checkbox"]:checked {
background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M5.707 7.293a1 1 0 0 0-1.414 1.414l2 2a1 1 0 0 0 1.414 0l4-4a1 1 0 0 0-1.414-1.414L7 8.586 5.707 7.293z'/%3e%3c/svg%3e"); background-image: url("data:image/svg+xml,%3csvg viewBox='0 0 16 16' fill='white' xmlns='http://www.w3.org/2000/svg'%3e%3cpath d='M5.707 7.293a1 1 0 0 0-1.414 1.414l2 2a1 1 0 0 0 1.414 0l4-4a1 1 0 0 0-1.414-1.414L7 8.586 5.707 7.293z'/%3e%3c/svg%3e");
} }
input[type="checkbox"]:focus { input[type="checkbox"]:focus {
outline: none; outline: none;
border-color: var(--fg); border-color: var(--fg);
} }
/* Code Snippets */ /* Code Snippets */
.token-line:not(:last-child) { .token-line:not(:last-child) {
min-height: 1.4rem; min-height: 1.4rem;
} }
article *:not(pre) > code { article *:not(pre) > code {
font-weight: 500; font-weight: 500;
font-family: var(--font-sans); font-family: var(--font-sans);
} }
article li > p { article li > p {
font-family: var(--font-mono); font-family: var(--font-mono);
display: inline-block; display: inline-block;
} }
article pre { article pre {
overflow-x: auto; overflow-x: auto;
border-radius: var(--inline-radius); border-radius: var(--inline-radius);
line-height: 1.8; line-height: 1.8;
padding: 1rem; padding: 1rem;
font-size: 0.875rem; font-size: 0.875rem;
} }
/* Linkable Headers */ /* Linkable Headers */
.header-link { .header-link {
color: inherit; color: inherit;
text-decoration: none; text-decoration: none;
} }
.header-link::after { .header-link::after {
opacity: 0; opacity: 0;
content: "#"; content: "#";
margin-left: var(--gap-half); margin-left: var(--gap-half);
} }
.header-link:hover::after { .header-link:hover::after {
opacity: 1; opacity: 1;
} }

View file

@ -2278,6 +2278,11 @@ lodash.camelcase@^4.3.0:
resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6" resolved "https://registry.yarnpkg.com/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz#b28aa6288a2b9fc651035c7711f65ab6190331a6"
integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY= integrity sha1-soqmKIorn8ZRA1x3EfZathkDMaY=
lodash.debounce@^4.0.8:
version "4.0.8"
resolved "https://registry.yarnpkg.com/lodash.debounce/-/lodash.debounce-4.0.8.tgz#82d79bff30a67c4005ffd5e2515300ad9ca4d7af"
integrity sha1-gteb/zCmfEAF/9XiUVMArZyk168=
lodash.merge@^4.6.2: lodash.merge@^4.6.2:
version "4.6.2" version "4.6.2"
resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a" resolved "https://registry.yarnpkg.com/lodash.merge/-/lodash.merge-4.6.2.tgz#558aa53b43b661e1925a0afdfa36a9a1085fe57a"