Skip to content

Commit 6584e87

Browse files
NovoutTahul
andauthored
feat: back-to-top and article badges (#122)
Co-authored-by: Yaël Guilloux <yael.guilloux@gmail.com>
1 parent 52dd186 commit 6584e87

9 files changed

Lines changed: 711 additions & 202 deletions

File tree

.starters/default/content/articles/2.configure.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -87,6 +87,12 @@ defineAppConfig({
8787
navigation: false, // possible value are : true | false
8888
position: 'center', // possible value are : 'none' | 'left' | 'center' | 'right'
8989
message: 'Follow me on' // string that will be displayed on the footer (leave empty or delete to disable)
90+
}
91+
// Disable back to top button: false
92+
backToTop: {
93+
text: 'Back to top',
94+
icon: 'material-symbols:arrow-upward'
95+
}
9096
}
9197
})
9298
```

.starters/default/content/articles/3.write-articles.md

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,22 @@ description: Another description
9191

9292
You are now ready to edit your article and create new ones!
9393

94+
## Optional Arguments
95+
96+
In the frontmatter block, you can pass additional options for displaying your article, such as displaying badges on the image:
97+
98+
```md
99+
---
100+
cover: path/to/cover
101+
date: 2022-08-23
102+
badges: [{
103+
color: 'white',
104+
bg: 'rgba(0, 0, 0, 0.3)',
105+
content: 'Technology'
106+
}]
107+
---
108+
```
109+
94110
## Read more
95111

96112
Alpine is a Nuxt theme using the Content module in `documentDriven` mode.

.starters/default/package.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
"lint": "eslint ."
1111
},
1212
"devDependencies": {
13-
"nuxt": "^3.4.3",
13+
"nuxt": "^3.5.0",
1414
"@nuxt-themes/alpine": "^1.5.4",
1515
"@nuxtjs/plausible": "^0.2.1",
1616
"@nuxt/devtools": "^0.4.6",

app.config.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,10 @@ export default defineAppConfig({
3636
},
3737
form: {
3838
successMessage: 'Message sent. Thank you!'
39+
},
40+
backToTop: {
41+
text: 'Back to top',
42+
icon: 'material-symbols:arrow-upward'
3943
}
4044
}
4145
})

components/content/ArticlesListItem.vue

Lines changed: 43 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ type Article = {
66
title: string
77
date: string
88
description: string
9+
badges?: { bg: string, text: string, content: string }[]
910
}
1011
1112
const props = defineProps({
@@ -30,15 +31,40 @@ const id = computed(() => {
3031
</script>
3132

3233
<template>
33-
<article v-if="article._path && article.title" :class="{ 'featured': featured }" :data-content-id="id">
34+
<article
35+
v-if="article._path && article.title"
36+
:class="{ 'featured': featured }"
37+
:data-content-id="id"
38+
>
3439
<div class="image">
40+
<div v-if="article?.badges">
41+
<span
42+
v-for="(badge, index) in article.badges"
43+
:key="index"
44+
:style="{
45+
backgroundColor: badge?.bg || 'rgba(0, 0, 0, 0.3)',
46+
color: badge?.color || 'white'
47+
}"
48+
>
49+
{{ typeof badge === 'string' ? badge : badge.content }}
50+
</span>
51+
</div>
3552
<NuxtLink :to="article._path">
36-
<NuxtImg v-if="article.cover" :src="article.cover" :alt="article.title" width="16" height="9" />
53+
<NuxtImg
54+
v-if="article.cover"
55+
:src="article.cover"
56+
:alt="article.title"
57+
width="16"
58+
height="9"
59+
/>
3760
</NuxtLink>
3861
</div>
3962

4063
<div class="content">
41-
<NuxtLink :to="article._path" class="headline">
64+
<NuxtLink
65+
:to="article._path"
66+
class="headline"
67+
>
4268
<h1>
4369
{{ article.title }}
4470
</h1>
@@ -74,6 +100,20 @@ css({
74100
},
75101
'.image': {
76102
flex: 1,
103+
div: {
104+
position: 'absolute',
105+
display: 'flex',
106+
flexWrap: true,
107+
gap: '{space.2}',
108+
marginTop: '{space.2}',
109+
marginLeft: '{space.2}',
110+
span: {
111+
padding: '{space.1}',
112+
borderRadius: '{radii.sm}',
113+
text: 'xs',
114+
fontWeight: 700
115+
}
116+
}
77117
},
78118
'.content': {
79119
display: 'flex',

layouts/article.vue

Lines changed: 43 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,51 @@
11
<template>
2-
<article>
2+
<article ref="article">
33
<!-- TODO: could be refactored as a transparent ButtonLink -->
4-
<NuxtLink :to="parentPath" class="back">
4+
<NuxtLink
5+
:to="parentPath"
6+
class="back"
7+
>
58
<Icon name="ph:arrow-left" />
6-
<span class="text-primary-900 dark:text-primary-100">
9+
<span>
710
Back
811
</span>
912
</NuxtLink>
1013
<header>
11-
<h1 v-if="page?.title" class="title">
14+
<h1
15+
v-if="page?.title"
16+
class="title"
17+
>
1218
{{ page.title }}
1319
</h1>
14-
<time v-if="page?.date" class="text-primary-700 dark:text-primary-400" :datetime="page.date">
20+
<time
21+
v-if="page?.date"
22+
:datetime="page.date"
23+
>
1524
{{ formatDate(page.date) }}
1625
</time>
1726
</header>
1827

1928
<div class="prose">
2029
<slot />
30+
<div
31+
v-if="alpine?.backToTop"
32+
class="back-to-top"
33+
>
34+
<ProseA @click.prevent.stop="onBackToTop">
35+
{{ alpine?.backToTop?.text || 'Back to top' }}
36+
<Icon :name="alpine?.backToTop?.icon || 'material-symbols:arrow-upward'" />
37+
</ProseA>
38+
</div>
2139
</div>
2240
</article>
2341
</template>
2442

2543
<script setup lang="ts">
2644
const { page } = useContent()
2745
const route = useRoute()
46+
const alpine = useAppConfig().alpine
47+
48+
const article = ref<HTMLElement | null>(null)
2849
2950
if (page.value && page.value.cover) {
3051
useHead({
@@ -41,6 +62,12 @@ const parentPath = computed(
4162
return pathTabl.join('/')
4263
}
4364
)
65+
66+
const onBackToTop = () => {
67+
article.value?.scrollIntoView({
68+
behavior: 'smooth'
69+
})
70+
}
4471
</script>
4572

4673
<style scoped lang="ts">
@@ -77,9 +104,19 @@ css({
77104
color: '{elements.text.secondary.color.static}'
78105
},
79106
'.prose': {
107+
'.back-to-top': {
108+
display: 'flex',
109+
justifyContent: 'flex-end',
110+
alignItems: 'center',
111+
width: '100%',
112+
a: {
113+
cursor: 'pointer',
114+
fontSize: '{text.lg.fontSize}'
115+
}
116+
},
80117
'& :deep(h1)': {
81118
display: 'none'
82-
}
119+
},
83120
}
84121
}
85122
})

nuxt.schema.ts

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -169,6 +169,15 @@ export default defineNuxtSchema({
169169
* Success message.
170170
*/
171171
successMessage: 'Message sent. Thank you!'
172+
},
173+
/**
174+
* Back to top button configuration.
175+
*
176+
* @studioIcon material-symbols:arrow-upward
177+
*/
178+
backToTop: {
179+
icon: 'material-symbols:arrow-upward',
180+
text: 'Back to top',
172181
}
173182
}
174183
}

package.json

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -36,13 +36,13 @@
3636
"devDependencies": {
3737
"@nuxt/eslint-config": "^0.1.1",
3838
"@nuxtjs/plausible": "^0.2.1",
39-
"@types/node": "^20.1.2",
39+
"@types/node": "^20.1.5",
4040
"eslint": "^8.40.0",
41-
"nuxt": "^3.4.3",
41+
"nuxt": "^3.5.0",
4242
"release-it": "^15.10.3",
4343
"typescript": "^5.0.4",
4444
"vite-plugin-inspect": "^0.7.26",
45-
"vue": "^3.2.47"
45+
"vue": "^3.3.2"
4646
},
4747
"pnpm": {
4848
"peerDependencyRules": {

0 commit comments

Comments
 (0)