Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
75 changes: 73 additions & 2 deletions app.vue
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ onMounted(() => {
});

// Track all sections that have an `id` applied
document.querySelectorAll('h2, h3').forEach((section) => {
document.querySelectorAll('h1, h2, h3, h4, h5, h6').forEach((section) => {
observer.observe(section);
});

Expand Down Expand Up @@ -117,6 +117,32 @@ onBeforeUnmount(() => {
window.removeEventListener('resize', handleResize);
});

// Keep scroll-padding-top equal to the sticky header's real height (it includes the
// breadcrumbs when they are visible, and changes across breakpoints), plus a small gap
let headerObserver: ResizeObserver | null = null

const updateScrollOffset = () => {
const header = document.querySelector('header')
const offset = header ? header.getBoundingClientRect().height : 0
document.documentElement.style.setProperty('--scroll-offset', `${Math.ceil(offset)}px`)
}

const observeHeader = () => {
headerObserver?.disconnect()
const header = document.querySelector('header')
if (header) headerObserver?.observe(header)
updateScrollOffset()
}

onMounted(() => {
headerObserver = new ResizeObserver(updateScrollOffset)
observeHeader()
})

watch(() => route.path, () => nextTick(observeHeader))

onBeforeUnmount(() => headerObserver?.disconnect())

onMounted(() => {
// Add the "Copy to Clipboard" button to all code blocks
document.querySelectorAll('pre').forEach(pre => {
Expand All @@ -141,7 +167,7 @@ const copyCode = (pre: HTMLElement) => {
<style>
html {
scroll-behavior: smooth !important;
scroll-padding-top: 16rem;
scroll-padding-top: calc(var(--scroll-offset, 8rem) + 1rem);
}

h1 {
Expand Down Expand Up @@ -242,6 +268,51 @@ a:hover {
color: theme('colors.oma-blue.400');
}

/* Heading anchor links keep the heading color instead of the link color */
:is(h1, h2, h3, h4, h5, h6) > a,
:is(h1, h2, h3, h4, h5, h6) > a:hover,
.dark :is(h1, h2, h3, h4, h5, h6) > a,
.dark :is(h1, h2, h3, h4, h5, h6) > a:hover {
color: inherit;
}

/* Heading anchor hover: heading slides right, a hash fades in where it used to be */
:is(h1, h2, h3, h4, h5, h6):has(> a) {
position: relative;
}

:is(h1, h2, h3, h4, h5, h6) > a {
display: inline-block;
transition: transform 0.3s ease;
}

:is(h1, h2, h3, h4, h5, h6):has(> a)::before {
content: '#';
position: absolute;
left: 0;
opacity: 0;
transform: translateX(-0.4em);
color: inherit;
pointer-events: none;
transition: opacity 0.3s ease, transform 0.3s ease;
}

:is(h1, h2, h3, h4, h5, h6):has(> a):hover > a {
transform: translateX(1em);
}

:is(h1, h2, h3, h4, h5, h6):has(> a):hover::before {
opacity: 1;
transform: translateX(0);
}

@media (prefers-reduced-motion: reduce) {
:is(h1, h2, h3, h4, h5, h6) > a,
:is(h1, h2, h3, h4, h5, h6):has(> a)::before {
transition: none;
}
}

/* End of Links */

/* CodeBlock */
Expand Down
28 changes: 16 additions & 12 deletions components/app/toc.vue
Original file line number Diff line number Diff line change
Expand Up @@ -7,23 +7,14 @@
</span>
</button>
<ul class="space-y-1 lg:block -ml-2">
<li v-for="(link, index) in page.body.toc.links" :key="index" class="space-y-1 lg:block"
:class="[isActive(link.id) ? ui.active : ui.normal]">
<li v-for="link in flatLinks" :key="link.id" class="space-y-1 lg:block"
:class="[isActive(link.id) ? ui.active : ui.normal, link.depth > 0 ? 'hidden lg:block' : '']"
:style="{ paddingLeft: `${link.depth * 0.75}rem` }">
<ULink :id="`toc-${link.id}`" :to="`${page.path}#${link.id}`"
:class="[ui.shadow, isActive(link.id) ? ui.link.active : ui.link.normal]"
class="not-prose pl-1 pr-1 text-black dark:text-golden">
{{ link.text }}
</ULink>
<ul v-if="link.children?.length > 0" class="space-y-1 hidden lg:block">
<li v-for="(subLink, subIndex) in link.children" :key="subIndex" class="space-y-1 lg:block"
:class="[isActive(subLink.id) ? ui.active : ui.normal]">
<ULink :id="`toc-${subLink.id}`" :to="`${page.path}#${subLink.id}`"
:class="[ui.shadow, isActive(subLink.id) ? ui.link.active : ui.link.normal]"
class="not-prose pl-1 pr-1 text-black dark:text-golden">
{{ subLink.text }}
</ULink>
</li>
</ul>
</li>
</ul>
</nav>
Expand Down Expand Up @@ -54,6 +45,19 @@ const props = withDefaults(defineProps<{

const { ui } = useUI("toc", toRef(props, "ui"), config);

// Flatten the nested toc (h1-h6) into a list, keeping each link's nesting depth for indentation
const flatLinks = computed(() => {
const result: { id: string, text: string, depth: number }[] = [];
const walk = (links: any[] = [], depth = 0) => {
for (const link of links) {
result.push({ id: link.id, text: link.text, depth });
walk(link.children, depth + 1);
}
};
walk(props.page?.body?.toc?.links);
return result;
});

const activeSection = ref<string | null>(null);

const isActive = (id: string) => {
Expand Down
13 changes: 10 additions & 3 deletions nuxt.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -22,15 +22,22 @@ export default defineNuxtConfig({
},
content: {
renderer: {
anchorLinks: false
// Make every heading (h1-h6) a clickable #anchor link
anchorLinks: { h1: true, h2: true, h3: true, h4: true, h5: true, h6: true }
},
highlight: {
theme: {
default: 'github-dark',
},
},
markdown: {
anchorLinks: false
build: {
markdown: {
// Table of contents lists h2 only (1 = h2, 2 = h2-h3, up to 5 = h2-h6)
toc: {
depth: 1,
searchDepth: 6,
},
},
},
api: {
baseURL: '/site-api/_content'
Expand Down
Loading