Start work on dark-mode, fix heading scroll position

This commit is contained in:
Riyyi
2025-03-31 14:56:59 +02:00
parent c26c74a2fb
commit 4e81ca7ad4
18 changed files with 245 additions and 53 deletions
+37 -35
View File
@@ -7,41 +7,6 @@ description: "This is a test article."
This is a test article. This is a test article.
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum a tempor
dolor. Nullam mattis sapien vel finibus dignissim. Etiam et diam ultrices,
aliquam enim nec, commodo sem. Cras ut faucibus risus. Suspendisse vel faucibus
ipsum. Duis vel orci nec arcu porttitor fermentum eu quis est. Phasellus elit
odio, elementum ac placerat at, feugiat sit amet sapien.
Vestibulum dapibus pharetra metus. Integer volutpat lacus nec enim euismod, id
dignissim felis rhoncus. Cras commodo tempus turpis, eu vehicula mi lacinia
eget. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per
inceptos himenaeos. Integer rhoncus dolor ut dolor gravida vestibulum. Ut
dignissim orci a ornare ullamcorper. Nam facilisis mauris sit amet nunc
fermentum pretium. Nullam in nisi at risus luctus viverra. Mauris non congue
dolor, vel finibus lectus. Nam quis leo pretium, sodales augue et, dictum sem.
Curabitur velit ante, imperdiet in eros eu, iaculis gravida risus. Nullam ut
feugiat eros, viverra vehicula sem. Aliquam finibus mi magna, eu fringilla
tellus ullamcorper vel. Fusce eget auctor mi. Mauris venenatis pellentesque
arcu. Nam ac diam sem. Nulla suscipit sed risus non vehicula. Cras molestie
lectus et tincidunt tempor. Ut tempus lorem id augue semper convallis. Aliquam
vel dui quis dolor cursus faucibus eget a mi. Curabitur tempus justo diam, a
facilisis ligula tincidunt viverra. Donec sodales quis dolor at dignissim.
Nullam placerat vitae urna quis bibendum.
Integer a magna a velit bibendum mollis. Donec lobortis molestie diam at rutrum.
Nunc viverra gravida metus at facilisis. Nulla sit amet erat sodales, commodo
elit sed, ultrices magna. Aliquam vel purus fringilla, gravida enim quis,
aliquam augue. Duis interdum et erat nec feugiat. Praesent vitae lacinia leo,
non suscipit purus.
Aenean massa magna, imperdiet id ex quis, mollis auctor lectus. Cras velit
nulla, volutpat eu neque id, semper venenatis urna. Integer in blandit ex, non
scelerisque nisi. Ut sagittis tincidunt enim at volutpat. Sed hendrerit metus ac
interdum laoreet. In rutrum turpis in nulla vulputate suscipit. Nunc dictum nisl
id magna laoreet dapibus.
## Component Rendering ## Component Rendering
::ExampleComponent ::ExampleComponent
@@ -78,3 +43,40 @@ Inline highlight with language `const code: string = 'highlighted code inline'`{
- An - An
- Unordered - Unordered
- List - List
## Heading With No Subheading
Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum a tempor
dolor. Nullam mattis sapien vel finibus dignissim. Etiam et diam ultrices,
aliquam enim nec, commodo sem. Cras ut faucibus risus. Suspendisse vel faucibus
ipsum. Duis vel orci nec arcu porttitor fermentum eu quis est. Phasellus elit
odio, elementum ac placerat at, feugiat sit amet sapien.
Vestibulum dapibus pharetra metus. Integer volutpat lacus nec enim euismod, id
dignissim felis rhoncus. Cras commodo tempus turpis, eu vehicula mi lacinia
eget. Class aptent taciti sociosqu ad litora torquent per conubia nostra, per
inceptos himenaeos. Integer rhoncus dolor ut dolor gravida vestibulum. Ut
dignissim orci a ornare ullamcorper. Nam facilisis mauris sit amet nunc
fermentum pretium. Nullam in nisi at risus luctus viverra. Mauris non congue
dolor, vel finibus lectus. Nam quis leo pretium, sodales augue et, dictum sem.
Curabitur velit ante, imperdiet in eros eu, iaculis gravida risus. Nullam ut
feugiat eros, viverra vehicula sem. Aliquam finibus mi magna, eu fringilla
tellus ullamcorper vel. Fusce eget auctor mi. Mauris venenatis pellentesque
arcu. Nam ac diam sem. Nulla suscipit sed risus non vehicula. Cras molestie
lectus et tincidunt tempor. Ut tempus lorem id augue semper convallis. Aliquam
vel dui quis dolor cursus faucibus eget a mi. Curabitur tempus justo diam, a
facilisis ligula tincidunt viverra. Donec sodales quis dolor at dignissim.
Nullam placerat vitae urna quis bibendum.
Integer a magna a velit bibendum mollis. Donec lobortis molestie diam at rutrum.
Nunc viverra gravida metus at facilisis. Nulla sit amet erat sodales, commodo
elit sed, ultrices magna. Aliquam vel purus fringilla, gravida enim quis,
aliquam augue. Duis interdum et erat nec feugiat. Praesent vitae lacinia leo,
non suscipit purus.
Aenean massa magna, imperdiet id ex quis, mollis auctor lectus. Cras velit
nulla, volutpat eu neque id, semper venenatis urna. Integer in blandit ex, non
scelerisque nisi. Ut sagittis tincidunt enim at volutpat. Sed hendrerit metus ac
interdum laoreet. In rutrum turpis in nulla vulputate suscipit. Nunc dictum nisl
id magna laoreet dapibus.
+9
View File
@@ -4,6 +4,9 @@ import ViteComponents from "unplugin-vue-components/vite"
// https://nuxt.com/docs/api/configuration/nuxt-config // https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({ export default defineNuxtConfig({
bootstrapVueNext: {
css: false
},
compatibilityDate: "2024-11-01", compatibilityDate: "2024-11-01",
content: { content: {
build: { build: {
@@ -14,12 +17,18 @@ export default defineNuxtConfig({
// https://github.com/shikijs/shiki/blob/main/packages/langs/package.json // https://github.com/shikijs/shiki/blob/main/packages/langs/package.json
"c", "cpp", "css", "html", "js", "json", "lua", "md", "mdc", "php", "shell", "ts", "vue", "yaml" "c", "cpp", "css", "html", "js", "json", "lua", "md", "mdc", "php", "shell", "ts", "vue", "yaml"
] ]
},
toc: {
title: "Table of Contents",
depth: 4, // include h4 headings
searchDepth: 2
} }
} }
} }
}, },
css: [ css: [
"bootstrap/dist/css/bootstrap.min.css", "bootstrap/dist/css/bootstrap.min.css",
"bootstrap-vue-next/dist/bootstrap-vue-next.css",
"~/assets/css/style.css" "~/assets/css/style.css"
], ],
devtools: { enabled: true }, devtools: { enabled: true },
+22 -9
View File
@@ -16,8 +16,9 @@
}, },
"devDependencies": { "devDependencies": {
"@bootstrap-vue-next/nuxt": "^0.28.3", "@bootstrap-vue-next/nuxt": "^0.28.3",
"@iconify-json/bi": "^1.2.2",
"@iconify-json/fa": "^1.2.1", "@iconify-json/fa": "^1.2.1",
"@iconify-json/fa-regular": "^1.2.1",
"@iconify-json/fa-solid": "^1.2.1",
"@nuxt/content": "^3.4.0", "@nuxt/content": "^3.4.0",
"@nuxt/eslint": "^1.2.0", "@nuxt/eslint": "^1.2.0",
"@types/bun": "latest", "@types/bun": "latest",
@@ -932,14 +933,6 @@
"url": "https://github.com/sponsors/nzakas" "url": "https://github.com/sponsors/nzakas"
} }
}, },
"node_modules/@iconify-json/bi": {
"version": "1.2.2",
"dev": true,
"license": "MIT",
"dependencies": {
"@iconify/types": "*"
}
},
"node_modules/@iconify-json/fa": { "node_modules/@iconify-json/fa": {
"version": "1.2.1", "version": "1.2.1",
"dev": true, "dev": true,
@@ -948,6 +941,26 @@
"@iconify/types": "*" "@iconify/types": "*"
} }
}, },
"node_modules/@iconify-json/fa-regular": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@iconify-json/fa-regular/-/fa-regular-1.2.1.tgz",
"integrity": "sha512-wKuHQchlr/zEd/Dl0ckEaWzgEXkWDs8zm5MczCX4Z7Ysd4xo9Iqk1t1EMJi3RtjrTE8Eh0s+PyHccH6qvZbNyQ==",
"dev": true,
"license": "CC-BY-4.0",
"dependencies": {
"@iconify/types": "*"
}
},
"node_modules/@iconify-json/fa-solid": {
"version": "1.2.1",
"resolved": "https://registry.npmjs.org/@iconify-json/fa-solid/-/fa-solid-1.2.1.tgz",
"integrity": "sha512-eEcnCb6yjRJ0bZrQQZOvO9P5PYCGujz2V8PpiIxBSlm6xqoVLNtOfUyPr6xZD4zWeW7H3cmWi2ZXSzqxQ5e4lw==",
"dev": true,
"license": "CC-BY-4.0",
"dependencies": {
"@iconify/types": "*"
}
},
"node_modules/@iconify/types": { "node_modules/@iconify/types": {
"version": "2.0.0", "version": "2.0.0",
"dev": true, "dev": true,
+2 -1
View File
@@ -19,8 +19,9 @@
}, },
"devDependencies": { "devDependencies": {
"@bootstrap-vue-next/nuxt": "^0.28.3", "@bootstrap-vue-next/nuxt": "^0.28.3",
"@iconify-json/bi": "^1.2.2",
"@iconify-json/fa": "^1.2.1", "@iconify-json/fa": "^1.2.1",
"@iconify-json/fa-regular": "^1.2.1",
"@iconify-json/fa-solid": "^1.2.1",
"@nuxt/content": "^3.4.0", "@nuxt/content": "^3.4.0",
"@nuxt/eslint": "^1.2.0", "@nuxt/eslint": "^1.2.0",
"@types/bun": "latest", "@types/bun": "latest",
+1 -1
View File
@@ -7,7 +7,7 @@
<script setup lang="ts"> <script setup lang="ts">
useHead({ useHead({
titleTemplate: (titleChunk) => { titleTemplate: (titleChunk: string): string => {
return titleChunk ? `${titleChunk} - website-vue` : 'website-vue'; return titleChunk ? `${titleChunk} - website-vue` : 'website-vue';
} }
}) })
-4
View File
@@ -15,10 +15,6 @@ body {
background-color: #f4f4f4; background-color: #f4f4f4;
} }
nav {
box-shadow: 0 .25rem .5rem rgba(0,0,0,.28) !important;
}
/* Main content */ /* Main content */
/*----------------------------------------*/ /*----------------------------------------*/
+2
View File
@@ -21,6 +21,8 @@ declare module 'vue' {
IFaHome: typeof import('~icons/fa/home')['default'] IFaHome: typeof import('~icons/fa/home')['default']
IFaLink: typeof import('~icons/fa/link')['default'] IFaLink: typeof import('~icons/fa/link')['default']
IFaLinkedinSquare: typeof import('~icons/fa/linkedin-square')['default'] IFaLinkedinSquare: typeof import('~icons/fa/linkedin-square')['default']
IFaMoonO: typeof import('~icons/fa/moon-o')['default']
IFaSolidSun: typeof import('~icons/fa-solid/sun')['default']
IMdiAccountBox: typeof import('~icons/mdi/account-box')['default'] IMdiAccountBox: typeof import('~icons/mdi/account-box')['default']
RouterLink: typeof import('vue-router')['RouterLink'] RouterLink: typeof import('vue-router')['RouterLink']
RouterView: typeof import('vue-router')['RouterView'] RouterView: typeof import('vue-router')['RouterView']
+10
View File
@@ -28,3 +28,13 @@
</BCollapse> </BCollapse>
</BNavbar> </BNavbar>
</template> </template>
<style scoped>
nav {
box-shadow: 0 .25rem .5rem rgba(0, 0, 0, .28);
}
[data-bs-theme=dark] nav {
box-shadow: 0 .25rem .5rem rgba(255, 0, 0, .28);
}
</style>
+23
View File
@@ -0,0 +1,23 @@
<template>
<div class="position-fixed corner">
<template v-if="store.colorMode === 'dark'">
<IFaSolidSun @click="store.toggleColorMode" />
</template>
<template v-else>
<IFaMoonO @click="store.toggleColorMode" />
</template>
</div>
</template>
<style scoped>
.corner {
right: 10px;
bottom: 10px;
}
</style>
<script setup lang="ts">
import { useStateStore } from "@/stores/stateStore";
const store = useStateStore();
</script>
+18
View File
@@ -0,0 +1,18 @@
<template>
<h1>
<span :id="props.id"></span>
<a v-if="props.id && generate" :href="`#${props.id}`">
<slot />
</a>
<slot v-else />
</h1>
</template>
<script setup lang="ts">
import { computed, useRuntimeConfig } from "#imports"
const props = defineProps<{ id?: string }>()
const { headings } = useRuntimeConfig().public.mdc
const generate = computed(() => props.id && ((typeof headings?.anchorLinks === "boolean" && headings?.anchorLinks === true) || (typeof headings?.anchorLinks === "object" && headings?.anchorLinks?.h1)))
</script>
+18
View File
@@ -0,0 +1,18 @@
<template>
<h2>
<span :id="props.id"></span>
<a v-if="props.id && generate" :href="`#${props.id}`">
<slot />
</a>
<slot v-else />
</h2>
</template>
<script setup lang="ts">
import { computed, useRuntimeConfig } from "#imports"
const props = defineProps<{ id?: string }>()
const { headings } = useRuntimeConfig().public.mdc
const generate = computed(() => props.id && ((typeof headings?.anchorLinks === "boolean" && headings?.anchorLinks === true) || (typeof headings?.anchorLinks === "object" && headings?.anchorLinks?.h2)))
</script>
+18
View File
@@ -0,0 +1,18 @@
<template>
<h3>
<span :id="props.id"></span>
<a v-if="props.id && generate" :href="`#${props.id}`">
<slot />
</a>
<slot v-else />
</h3>
</template>
<script setup lang="ts">
import { computed, useRuntimeConfig } from "#imports"
const props = defineProps<{ id?: string }>()
const { headings } = useRuntimeConfig().public.mdc
const generate = computed(() => props.id && ((typeof headings?.anchorLinks === "boolean" && headings?.anchorLinks === true) || (typeof headings?.anchorLinks === "object" && headings?.anchorLinks?.h3)))
</script>
+18
View File
@@ -0,0 +1,18 @@
<template>
<h4>
<span :id="props.id"></span>
<a v-if="props.id && generate" :href="`#${props.id}`">
<slot />
</a>
<slot v-else />
</h4>
</template>
<script setup lang="ts">
import { computed, useRuntimeConfig } from "#imports"
const props = defineProps<{ id?: string }>()
const { headings } = useRuntimeConfig().public.mdc
const generate = computed(() => props.id && ((typeof headings?.anchorLinks === "boolean" && headings?.anchorLinks === true) || (typeof headings?.anchorLinks === "object" && headings?.anchorLinks?.h4)))
</script>
+18
View File
@@ -0,0 +1,18 @@
<template>
<h5>
<span :id="props.id"></span>
<a v-if="props.id && generate" :href="`#${props.id}`">
<slot />
</a>
<slot v-else />
</h5>
</template>
<script setup lang="ts">
import { computed, useRuntimeConfig } from "#imports"
const props = defineProps<{ id?: string }>()
const { headings } = useRuntimeConfig().public.mdc
const generate = computed(() => props.id && ((typeof headings?.anchorLinks === "boolean" && headings?.anchorLinks === true) || (typeof headings?.anchorLinks === "object" && headings?.anchorLinks?.h5)))
</script>
+18
View File
@@ -0,0 +1,18 @@
<template>
<h6>
<span :id="props.id"></span>
<a v-if="props.id && generate" :href="`#${props.id}`">
<slot />
</a>
<slot v-else />
</h6>
</template>
<script setup lang="ts">
import { computed, useRuntimeConfig } from "#imports"
const props = defineProps<{ id?: string }>()
const { headings } = useRuntimeConfig().public.mdc
const generate = computed(() => props.id && ((typeof headings?.anchorLinks === "boolean" && headings?.anchorLinks === true) || (typeof headings?.anchorLinks === "object" && headings?.anchorLinks?.h6)))
</script>
+1
View File
@@ -10,4 +10,5 @@
</div> </div>
<SharedFooter /> <SharedFooter />
</div> </div>
<ToggleColorMode />
</template> </template>
+12 -3
View File
@@ -15,8 +15,19 @@
<style scoped> <style scoped>
/* Target element in child components with :deep */ /* Target element in child components with :deep */
/* Select any <h> */
:deep(:is(h1, h2, h3, h4, h5, h6):has(span)) {
position: relative;
}
/* Select any <span> inside a <h> */
:deep(:is(h1, h2, h3, h4, h5, h6) span) {
position: absolute;
top: -65px;
}
/* Select any <a> inside a <h> */ /* Select any <a> inside a <h> */
:deep(:is(h1, h2, h3, h4, h5, h6) > a) { :deep(:is(h1, h2, h3, h4, h5, h6) a) {
color: inherit; color: inherit;
text-decoration: none; text-decoration: none;
} }
@@ -50,8 +61,6 @@
<script setup lang="ts"> <script setup lang="ts">
const { params } = useRoute(); const { params } = useRoute();
console.log(params);
const { data: article } = await useAsyncData( const { data: article } = await useAsyncData(
`article-${params.slug}`, `article-${params.slug}`,
() => queryCollection("content").path("/articles/" + params.slug).first() () => queryCollection("content").path("/articles/" + params.slug).first()
+18
View File
@@ -0,0 +1,18 @@
import { defineStore } from "pinia"
import { ref } from "vue";
import { useColorMode } from 'bootstrap-vue-next'
export const useStateStore = defineStore("state", () => {
const colorMode = ref<string>("light");
const toggleColorMode = (): void => {
colorMode.value = colorMode.value === "dark" ? "light" : "dark";
const html = document.querySelector("html") as HTMLElement;
html.setAttribute('data-bs-theme', colorMode.value);
};
return { colorMode, toggleColorMode }
}, {
persist: process.env.NODE_ENV === 'development' ? true : false,
})