feat: typography, toc
This commit is contained in:
parent
ea68d4f02f
commit
8fe228e243
16 changed files with 194 additions and 311 deletions
|
@ -4,7 +4,7 @@ import SocialIcons from './SocialIcons.astro'
|
|||
import { ModeToggle } from '@components/ui/mode-toggle'
|
||||
---
|
||||
|
||||
<footer class="py-8">
|
||||
<footer class="py-4">
|
||||
<Container>
|
||||
<div class="flex justify-between items-center">
|
||||
<div class="flex items-center space-x-4">
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
---
|
||||
import '../styles/global.css'
|
||||
import '../styles/katex.css'
|
||||
import '../styles/typography.css'
|
||||
|
||||
import '@fontsource/geist-sans'
|
||||
import '@fontsource/geist-mono'
|
||||
|
|
|
@ -11,21 +11,23 @@ const items = [
|
|||
]
|
||||
---
|
||||
|
||||
<header class="sticky top-0 z-10" transition:persist>
|
||||
<header class="sticky top-0 z-10 bg-background/50 backdrop-blur-md" transition:persist>
|
||||
<Container>
|
||||
<div class="flex items-center justify-between py-4">
|
||||
<Link href="/" underline={false}>
|
||||
{SITE.TITLE}<span class="ml-1">🍄</span>
|
||||
<Link href="/" class="text-xl font-semibold hover:text-primary transition-colors duration-300">
|
||||
{SITE.TITLE}
|
||||
</Link>
|
||||
<nav class="flex items-center space-x-1 text-sm">
|
||||
{
|
||||
items.map((item, index) => (
|
||||
<>
|
||||
<Link href={item.href}>{item.label}</Link>
|
||||
{index < items.length - 1 && <span class="text-gray-400">/</span>}
|
||||
</>
|
||||
))
|
||||
}
|
||||
<nav class="flex items-center gap-4 md:gap-6 text-sm">
|
||||
{items.map((item, index) => (
|
||||
<Fragment key={item.href}>
|
||||
<Link
|
||||
href={item.href}
|
||||
class="transition-colors hover:text-foreground/80 text-foreground/60 capitalize"
|
||||
>
|
||||
{item.label}
|
||||
</Link>
|
||||
</Fragment>
|
||||
))}
|
||||
</nav>
|
||||
</div>
|
||||
</Container>
|
||||
|
|
|
@ -4,22 +4,22 @@ import { cn } from '@lib/utils'
|
|||
type Props = {
|
||||
href: string
|
||||
external?: boolean
|
||||
underline?: boolean
|
||||
group?: boolean
|
||||
class?: string
|
||||
'data-heading'?: string
|
||||
}
|
||||
|
||||
const { href, external, underline = true, group = false, ...rest } = Astro.props
|
||||
const { href, external, class: className, 'data-heading': dataHeading, ...rest } = Astro.props
|
||||
---
|
||||
|
||||
<a
|
||||
href={href}
|
||||
target={external ? '_blank' : '_self'}
|
||||
class={cn(
|
||||
'inline-block transition-colors duration-300 ease-in-out',
|
||||
underline && 'underline underline-offset-[3px]',
|
||||
group && 'group',
|
||||
'inline-block transition-colors duration-300 ease-in-out hover:underline underline-offset-[3px]',
|
||||
className
|
||||
)}
|
||||
data-heading={dataHeading}
|
||||
{...rest}
|
||||
>
|
||||
<slot />
|
||||
</a>
|
||||
</a>
|
|
@ -27,17 +27,17 @@ function buildToc(headings: Heading[]) {
|
|||
}
|
||||
---
|
||||
|
||||
<details open class="rounded-lg border">
|
||||
<summary>Table of Contents</summary>
|
||||
<details open class="block xl:hidden rounded-lg border p-3 mb-8">
|
||||
<summary class="text-xl font-semibold">Table of Contents</summary>
|
||||
<nav>
|
||||
<ul class="py-3">
|
||||
{toc.map((heading) => <TableOfContentsHeading heading={heading} />)}
|
||||
</ul>
|
||||
</nav>
|
||||
</details>
|
||||
|
||||
<style>
|
||||
summary {
|
||||
@apply cursor-pointer rounded-t-lg px-3 py-1.5 font-medium transition-colors;
|
||||
}
|
||||
</style>
|
||||
<nav class="sticky top-16 hidden xl:block h-0 w-[calc(50vw-50%-4rem)] overflow-wrap-break-word text-xs leading-4 translate-x-[calc(-100%-2em)]">
|
||||
<h2 class="text-xl font-semibold mb-4">Table of Contents</h2>
|
||||
<ul class="space-y-2">
|
||||
{toc.map((heading) => <TableOfContentsHeading heading={heading} />)}
|
||||
</ul>
|
||||
</nav>
|
||||
|
|
|
@ -2,18 +2,16 @@
|
|||
import type { Heading } from './TableOfContents.astro'
|
||||
import Link from './Link.astro'
|
||||
|
||||
// https://kld.dev/building-table-of-contents/
|
||||
|
||||
const { heading } = Astro.props
|
||||
---
|
||||
|
||||
<li class="list-inside list-disc px-6 py-1.5 text-sm">
|
||||
<Link href={'#' + heading.slug} underline>
|
||||
<li class="list-inside list-disc px-6 py-1.5 xl:list-none xl:p-0 text-sm">
|
||||
<Link href={'#' + heading.slug} class="toc-link" data-heading={heading.slug}>
|
||||
{heading.text}
|
||||
</Link>
|
||||
{
|
||||
heading.subheadings.length > 0 && (
|
||||
<ul class="translate-x-3">
|
||||
<ul class="translate-x-3 xl:translate-x-0 xl:ml-4 xl:mt-2 xl:space-y-2">
|
||||
{heading.subheadings.map((subheading: Heading) => (
|
||||
<Astro.self heading={subheading} />
|
||||
))}
|
||||
|
@ -21,3 +19,31 @@ const { heading } = Astro.props
|
|||
)
|
||||
}
|
||||
</li>
|
||||
|
||||
<script>
|
||||
function updateActiveHeading() {
|
||||
const headings = document.querySelectorAll('h2, h3, h4, h5, h6');
|
||||
const tocLinks = document.querySelectorAll('.toc-link');
|
||||
|
||||
let currentHeading = '';
|
||||
|
||||
headings.forEach(heading => {
|
||||
const top = heading.getBoundingClientRect().top;
|
||||
if (top < 100) {
|
||||
currentHeading = heading.id;
|
||||
}
|
||||
});
|
||||
|
||||
tocLinks.forEach(link => {
|
||||
const headingSlug = link.getAttribute('data-heading');
|
||||
if (headingSlug === currentHeading) {
|
||||
link.classList.add('underline');
|
||||
} else {
|
||||
link.classList.remove('underline');
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', updateActiveHeading);
|
||||
window.addEventListener('load', updateActiveHeading);
|
||||
</script>
|
|
@ -28,7 +28,7 @@ export function ModeToggle() {
|
|||
}, [theme])
|
||||
|
||||
return (
|
||||
<DropdownMenu>
|
||||
<DropdownMenu modal={false}>
|
||||
<DropdownMenuTrigger asChild>
|
||||
<Button variant="outline" size="icon">
|
||||
<Sun className="h-[1.2rem] w-[1.2rem] rotate-0 scale-100 transition-all dark:-rotate-90 dark:scale-0" />
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue