rework layout and add table of contents

This commit is contained in:
Joseph Montanaro 2023-08-21 22:16:17 -07:00
parent 33d6838dc4
commit 5817d94043
8 changed files with 174 additions and 65 deletions

41
package-lock.json generated
View File

@ -11,6 +11,7 @@
"@sveltejs/adapter-static": "^1.0.0-next.21", "@sveltejs/adapter-static": "^1.0.0-next.21",
"@sveltejs/kit": "next", "@sveltejs/kit": "next",
"hast-util-to-text": "^3.1.2", "hast-util-to-text": "^3.1.2",
"mdast-util-to-string": "^4.0.0",
"mdsvex": "^0.9.8", "mdsvex": "^0.9.8",
"node-sass": "^6.0.1", "node-sass": "^6.0.1",
"svelte": "^3.42.6", "svelte": "^3.42.6",
@ -171,6 +172,15 @@
"@types/unist": "^2" "@types/unist": "^2"
} }
}, },
"node_modules/@types/mdast": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.0.tgz",
"integrity": "sha512-YLeG8CujC9adtj/kuDzq1N4tCDYKoZ5l/bnjq8d74+t/3q/tHquJOJKUQXJrLCflOHpKjXgcI/a929gpmLOEng==",
"dev": true,
"dependencies": {
"@types/unist": "*"
}
},
"node_modules/@types/minimist": { "node_modules/@types/minimist": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz",
@ -1773,6 +1783,19 @@
"url": "https://github.com/sponsors/sindresorhus" "url": "https://github.com/sponsors/sindresorhus"
} }
}, },
"node_modules/mdast-util-to-string": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
"integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
"dev": true,
"dependencies": {
"@types/mdast": "^4.0.0"
},
"funding": {
"type": "opencollective",
"url": "https://opencollective.com/unified"
}
},
"node_modules/mdsvex": { "node_modules/mdsvex": {
"version": "0.9.8", "version": "0.9.8",
"resolved": "https://registry.npmjs.org/mdsvex/-/mdsvex-0.9.8.tgz", "resolved": "https://registry.npmjs.org/mdsvex/-/mdsvex-0.9.8.tgz",
@ -3613,6 +3636,15 @@
"@types/unist": "^2" "@types/unist": "^2"
} }
}, },
"@types/mdast": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/mdast/-/mdast-4.0.0.tgz",
"integrity": "sha512-YLeG8CujC9adtj/kuDzq1N4tCDYKoZ5l/bnjq8d74+t/3q/tHquJOJKUQXJrLCflOHpKjXgcI/a929gpmLOEng==",
"dev": true,
"requires": {
"@types/unist": "*"
}
},
"@types/minimist": { "@types/minimist": {
"version": "1.2.2", "version": "1.2.2",
"resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz", "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz",
@ -4778,6 +4810,15 @@
"integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==", "integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==",
"dev": true "dev": true
}, },
"mdast-util-to-string": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/mdast-util-to-string/-/mdast-util-to-string-4.0.0.tgz",
"integrity": "sha512-0H44vDimn51F0YwvxSJSm0eCDOJTRlmN0R1yBh4HLj9wiV1Dn0QoXGbvFAWj2hSItVTlCmBF1hqKlIyUBVFLPg==",
"dev": true,
"requires": {
"@types/mdast": "^4.0.0"
}
},
"mdsvex": { "mdsvex": {
"version": "0.9.8", "version": "0.9.8",
"resolved": "https://registry.npmjs.org/mdsvex/-/mdsvex-0.9.8.tgz", "resolved": "https://registry.npmjs.org/mdsvex/-/mdsvex-0.9.8.tgz",

View File

@ -15,7 +15,8 @@
"svelte-preprocess": "^4.9.8", "svelte-preprocess": "^4.9.8",
"unist-util-visit": "^5.0.0", "unist-util-visit": "^5.0.0",
"unist-util-find": "^2.0.0", "unist-util-find": "^2.0.0",
"hast-util-to-text": "^3.1.2" "hast-util-to-text": "^3.1.2",
"mdast-util-to-string": "^4.0.0"
}, },
"type": "module" "type": "module"
} }

View File

@ -16,11 +16,31 @@
</script> </script>
<style> <style>
.page {
display: grid;
grid-template-columns: minmax(0, 1fr) minmax(0, var(--content-width)) minmax(0, 1fr);
padding: 0 0.5rem;
}
.title {
grid-column: 2 / 3;
}
.left-gutter {
grid-column: 1 / 2;
justify-self: end;
}
.subtitle { .subtitle {
font-size: 0.9em; font-size: 0.9em;
font-style: italic; font-style: italic;
margin-top: -0.5rem; margin-top: -0.5rem;
} }
.post {
grid-column: 2 / 3;
}
</style> </style>
<svelte:head> <svelte:head>
@ -28,9 +48,17 @@
<link rel="stylesheet" href="/prism-dracula.css" /> <link rel="stylesheet" href="/prism-dracula.css" />
</svelte:head> </svelte:head>
<div id="post"> <div class="page">
<h1 id="{makeSlug(title)}">{title}</h1> <div class="title">
<p class="subtitle">{formatDate(date)}</p> <h1 id="{makeSlug(title)}">{title}</h1>
<Toc items={toc} /> <p class="subtitle">{formatDate(date)}</p>
<slot></slot> </div>
</div>
<div class="left-gutter">
<Toc items={toc} />
</div>
<div class="post">
<slot></slot>
</div>
</div>

View File

@ -115,18 +115,6 @@
} }
} }
// /* slight tweaks for in between state */
// @media (min-width: 52.5em) and (max-width: 70em) {
// .sidenote {
// padding-left: calc(50vw - 19rem);
// }
// }
// @media (max-width: 52.5em) {
// .sidenote {
// padding-left: 2rem;
// }
// }
</style> </style>
<script context="module"> <script context="module">

View File

@ -1,32 +1,66 @@
<script> <script>
import { onMount } from 'svelte';
import { makeSlug } from '$lib/utils.js'; import { makeSlug } from '$lib/utils.js';
export let items; export let items;
console.log(items); items.forEach(i => i.slug = makeSlug(i.text));
const selector = 'h1[id], h2[id], h3[id], h4[id], h6[id]';
let currentHeadingSlug = null;
function setCurrentHeading() {
for (const h of document.querySelectorAll(selector)) {
const yPos = h.getBoundingClientRect().y;
if (yPos > (window.innerHeight / 3)) {
break;
}
currentHeadingSlug = h.id;
}
}
onMount (() => {
document.addEventListener('scroll', setCurrentHeading);
setCurrentHeading();
});
</script> </script>
<style> <style>
/* move everything out to the left and center it vertically */ /* move everything out to the left and center it vertically */
#toc { #toc {
position: fixed; position: sticky;
left: 1rem; top: 1.5rem;
/* setting top at 50vh sticks it halfway down the viewport, margin-right: 4rem;
then translating back up by 50% gets it vertically centered
even with a dynamic width */ max-width: 14rem;
top: 50vh;
transform: translateY(-50%);
color: var(--content-color-faded); color: var(--content-color-faded);
opacity: 0;
animation: fade-in 600ms ease-out;
animation-delay: 500ms;
animation-fill-mode: forwards;
} }
/* margin-left is to match the list items; this allows the accent bar @keyframes fade-in {
for the current item to appear to float out in space */ from {opacity: 0}
to {opacity: 100%}
}
/* margin-left is to match the padding on the top-level list items,
but here it needs to be margin so that the border is also shifted */
h5 { h5 {
margin-left: 0.5rem;
padding-bottom: 0.25em;
margin-bottom: 0.25em;
border-bottom: 0.1em solid currentcolor;
font-variant: petite-caps; font-variant: petite-caps;
font-weight: 500;
max-width: fit-content;
margin-top: 0;
/* 0.5rem for indent, 0.1rem for border */
margin-left: 0.6rem;
margin-bottom: 0.25em;
padding-right: 1.5rem;
padding-bottom: 0.25em;
border-bottom: 1px solid currentcolor;
} }
ul { ul {
@ -35,13 +69,21 @@
list-style: none; list-style: none;
} }
/* margin for indentation, padding so that the accent bar for the current
item isn't right on top of it */
li { li {
margin-left: var(--indent, 0); margin-left: var(--indent, 0);
padding-left: 0.5rem; padding-left: 0.5rem;
margin-bottom: 0.15rem; margin-bottom: 0.15rem;
font-size: 0.9rem; font-size: 0.9rem;
border-left: 0.1rem solid transparent;
}
li:hover {
color: var(--content-color);
border-left: 0.1rem solid var(--accent-color);
} }
li.current { li.current {
color: var(--content-color);
border-left: 0.1rem solid var(--accent-color); border-left: 0.1rem solid var(--accent-color);
} }
@ -49,18 +91,20 @@
color: inherit; color: inherit;
text-decoration: none; text-decoration: none;
} }
a:hover {
text-decoration: underline;
}
</style> </style>
<div id="toc"> <div id="toc">
<h5>Table of Contents</h5> <h5>
<span class="heading">Contents</span>
</h5>
<ul> <ul>
{#each items as item} {#each items as item}
<li style:--indent="{(item.depth - 2) * 0.75}em"> <li
<a href="#{makeSlug(item.text)}">{item.text}</a> style:--indent="{(item.depth - 2) * 0.75}em"
class:current={item.slug === currentHeadingSlug}
>
<a href="#{item.slug}">{item.text}</a>
</li> </li>
{/each} {/each}
</ul> </ul>

View File

@ -1,39 +1,43 @@
<style> <script>
:global(main) { import Toc from '$lib/Toc.svelte';
--content-width: 42rem;
box-sizing: border-box;
max-width: var(--content-width);
margin: 0 auto;
padding: 0 15px;
}
#header { const items = [
{depth: 2, text: 'The Suboptimal Solution: Absolute Positioning'},
{depth: 2, text: 'CSS Wizardry'},
{depth: 2, text: 'Implementation'},
];
</script>
<style>
.header {
grid-column-start: 1;
grid-column-end: 4;
background-color: #4f5f68; background-color: #4f5f68;
} }
#nav-main { nav {
max-width: 30rem; max-width: 30rem;
margin: 0 auto; margin: 0 auto;
display: grid; display: flex;
grid-template-columns: repeat(3, minmax(6rem, 8rem)); justify-content: space-around;
justify-content: space-between;
} }
#nav-main a { nav a {
width: 8rem;
min-width: 6rem;
font-size: 1.5rem; font-size: 1.5rem;
color: white; color: white;
text-decoration: none; text-decoration: none;
text-align: center; text-align: center;
padding: 0.25rem 0; padding: 0.25rem 0;
} }
nav a:hover {
#nav-main a:hover {
background-color: #00000025; background-color: #00000025;
} }
</style> </style>
<div id="header"> <div class="header">
<nav id="nav-main"> <nav>
<a sveltekit:prefetch href="/">Home</a> <a sveltekit:prefetch href="/">Home</a>
<a sveltekit:prefetch href="/posts">Posts</a> <a sveltekit:prefetch href="/posts">Posts</a>
<a sveltekit:prefetch href="/">About</a> <a sveltekit:prefetch href="/">About</a>

View File

@ -29,10 +29,12 @@ html {
line-height: var(--content-line-height); line-height: var(--content-line-height);
letter-spacing: -0.005em; letter-spacing: -0.005em;
color: var(--content-color); color: var(--content-color);
box-sizing: border-box;
} }
body { body {
margin: 0; margin: 0;
--content-width: 42rem;
} }
h1, h2, h3, h4, h5, h6 { h1, h2, h3, h4, h5, h6 {

View File

@ -1,9 +1,9 @@
import { mdsvex } from 'mdsvex'; import { mdsvex } from 'mdsvex';
import staticAdapter from '@sveltejs/adapter-static'; import staticAdapter from '@sveltejs/adapter-static';
import svp from 'svelte-preprocess'; import svp from 'svelte-preprocess';
// import slug from './src/lib/slug.js';
// import { addDropcaps } from './src/lib/dropcapify.js'; import { localRemark } from './src/plugins/remark.js';
import { localPlugins } from './src/plugins/rehype.js'; import { localRehype } from './src/plugins/rehype.js';
const config = { const config = {
@ -11,7 +11,8 @@ const config = {
preprocess: [ preprocess: [
mdsvex({ mdsvex({
layout: './src/lib/Post.svelte', layout: './src/lib/Post.svelte',
rehypePlugins: [localPlugins], remarkPlugins: [localRemark],
rehypePlugins: [localRehype],
}), }),
svp.scss(), svp.scss(),
], ],