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/kit": "next",
"hast-util-to-text": "^3.1.2",
"mdast-util-to-string": "^4.0.0",
"mdsvex": "^0.9.8",
"node-sass": "^6.0.1",
"svelte": "^3.42.6",
@ -171,6 +172,15 @@
"@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": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz",
@ -1773,6 +1783,19 @@
"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": {
"version": "0.9.8",
"resolved": "https://registry.npmjs.org/mdsvex/-/mdsvex-0.9.8.tgz",
@ -3613,6 +3636,15 @@
"@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": {
"version": "1.2.2",
"resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.2.tgz",
@ -4778,6 +4810,15 @@
"integrity": "sha512-hdN1wVrZbb29eBGiGjJbeP8JbKjq1urkHJ/LIP/NY48MZ1QVXUsQBV1G1zvYFHn1XE06cwjBsOI2K3Ulnj1YXQ==",
"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": {
"version": "0.9.8",
"resolved": "https://registry.npmjs.org/mdsvex/-/mdsvex-0.9.8.tgz",

View File

@ -15,7 +15,8 @@
"svelte-preprocess": "^4.9.8",
"unist-util-visit": "^5.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"
}

View File

@ -16,11 +16,31 @@
</script>
<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 {
font-size: 0.9em;
font-style: italic;
margin-top: -0.5rem;
}
.post {
grid-column: 2 / 3;
}
</style>
<svelte:head>
@ -28,9 +48,17 @@
<link rel="stylesheet" href="/prism-dracula.css" />
</svelte:head>
<div id="post">
<div class="page">
<div class="title">
<h1 id="{makeSlug(title)}">{title}</h1>
<p class="subtitle">{formatDate(date)}</p>
</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>
<script context="module">

View File

@ -1,32 +1,66 @@
<script>
import { onMount } from 'svelte';
import { makeSlug } from '$lib/utils.js';
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>
<style>
/* move everything out to the left and center it vertically */
#toc {
position: fixed;
left: 1rem;
/* setting top at 50vh sticks it halfway down the viewport,
then translating back up by 50% gets it vertically centered
even with a dynamic width */
top: 50vh;
transform: translateY(-50%);
position: sticky;
top: 1.5rem;
margin-right: 4rem;
max-width: 14rem;
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
for the current item to appear to float out in space */
@keyframes fade-in {
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 {
margin-left: 0.5rem;
padding-bottom: 0.25em;
margin-bottom: 0.25em;
border-bottom: 0.1em solid currentcolor;
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 {
@ -35,13 +69,21 @@
list-style: none;
}
/* margin for indentation, padding so that the accent bar for the current
item isn't right on top of it */
li {
margin-left: var(--indent, 0);
padding-left: 0.5rem;
margin-bottom: 0.15rem;
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 {
color: var(--content-color);
border-left: 0.1rem solid var(--accent-color);
}
@ -49,18 +91,20 @@
color: inherit;
text-decoration: none;
}
a:hover {
text-decoration: underline;
}
</style>
<div id="toc">
<h5>Table of Contents</h5>
<h5>
<span class="heading">Contents</span>
</h5>
<ul>
{#each items as item}
<li style:--indent="{(item.depth - 2) * 0.75}em">
<a href="#{makeSlug(item.text)}">{item.text}</a>
<li
style:--indent="{(item.depth - 2) * 0.75}em"
class:current={item.slug === currentHeadingSlug}
>
<a href="#{item.slug}">{item.text}</a>
</li>
{/each}
</ul>

View File

@ -1,39 +1,43 @@
<style>
:global(main) {
--content-width: 42rem;
box-sizing: border-box;
max-width: var(--content-width);
margin: 0 auto;
padding: 0 15px;
}
<script>
import Toc from '$lib/Toc.svelte';
#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;
}
#nav-main {
nav {
max-width: 30rem;
margin: 0 auto;
display: grid;
grid-template-columns: repeat(3, minmax(6rem, 8rem));
justify-content: space-between;
display: flex;
justify-content: space-around;
}
#nav-main a {
nav a {
width: 8rem;
min-width: 6rem;
font-size: 1.5rem;
color: white;
text-decoration: none;
text-align: center;
padding: 0.25rem 0;
}
#nav-main a:hover {
nav a:hover {
background-color: #00000025;
}
</style>
<div id="header">
<nav id="nav-main">
<div class="header">
<nav>
<a sveltekit:prefetch href="/">Home</a>
<a sveltekit:prefetch href="/posts">Posts</a>
<a sveltekit:prefetch href="/">About</a>

View File

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

View File

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