Skip to content

Commit 6472bb0

Browse files
committed
feat: customize homepage with personal content and Nuxt UI v3 components
1 parent 5f6965c commit 6472bb0

3 files changed

Lines changed: 236 additions & 210 deletions

File tree

app/pages/index.vue

Lines changed: 85 additions & 58 deletions
Original file line numberDiff line numberDiff line change
@@ -16,82 +16,109 @@ useSeoMeta({
1616
<template>
1717
<div v-if="page">
1818
<UPageHero
19-
:title="page.title"
20-
:description="page.description"
21-
:links="page.hero.links"
19+
:title="page.hero?.title || page.title"
20+
:description="page.hero?.description || page.description"
21+
:links="page.hero?.links"
22+
:ui="{
23+
title: 'text-4xl sm:text-5xl lg:text-6xl text-center',
24+
description: 'text-lg sm:text-xl text-gray-600 dark:text-gray-300 text-center',
25+
headline: 'mb-8'
26+
}"
2227
>
23-
<template #top>
24-
<HeroBackground />
25-
</template>
26-
27-
<template #title>
28-
<MDC
29-
:value="page.title"
30-
unwrap="p"
28+
<template #headline>
29+
<NuxtImg
30+
src="/images/profile.png"
31+
alt="Picture of Alexandre Nédélec"
32+
width="288"
33+
height="288"
34+
class="mx-auto h-56 w-56 md:h-72 md:w-72 rounded-full object-cover"
3135
/>
3236
</template>
33-
34-
<PromotionalVideo />
3537
</UPageHero>
3638

3739
<UPageSection
38-
v-for="(section, index) in page.sections"
39-
:key="index"
40-
:title="section.title"
41-
:description="section.description"
42-
:orientation="section.orientation"
43-
:reverse="section.reverse"
44-
:features="section.features"
45-
>
46-
<ImagePlaceholder />
47-
</UPageSection>
48-
49-
<UPageSection
50-
:title="page.features.title"
51-
:description="page.features.description"
40+
v-if="page.roles"
41+
:title="page.roles.title"
42+
:description="page.roles.description"
43+
:ui="{
44+
title: 'text-3xl sm:text-4xl',
45+
description: 'text-lg text-gray-600 dark:text-gray-300'
46+
}"
5247
>
5348
<UPageGrid>
5449
<UPageCard
55-
v-for="(item, index) in page.features.items"
50+
v-for="(item, index) in page.roles.items"
5651
:key="index"
57-
v-bind="item"
52+
:title="item.title"
53+
:description="item.description"
54+
:icon="item.icon"
55+
:to="item.to"
56+
:target="item.target"
5857
spotlight
58+
:ui="{
59+
title: 'text-xl font-semibold',
60+
description: 'text-base text-gray-600 dark:text-gray-400',
61+
leadingIcon: 'size-8'
62+
}"
5963
/>
6064
</UPageGrid>
6165
</UPageSection>
6266

67+
<UPageSection v-if="page.socials">
68+
<div class="text-center">
69+
<h2 class="text-3xl sm:text-4xl font-bold text-gray-900 dark:text-white mb-8">{{ page.socials.title }}</h2>
70+
<div class="flex flex-wrap justify-center gap-8">
71+
<UButton
72+
v-for="(social, index) in page.socials.links"
73+
:key="index"
74+
:to="social.to"
75+
target="_blank"
76+
color="neutral"
77+
variant="ghost"
78+
size="xl"
79+
:icon="social.icon"
80+
square
81+
:aria-label="social.label"
82+
:ui="{ leadingIcon: 'size-10' }"
83+
/>
84+
</div>
85+
</div>
86+
</UPageSection>
87+
6388
<UPageSection
64-
id="testimonials"
65-
:headline="page.testimonials.headline"
66-
:title="page.testimonials.title"
67-
:description="page.testimonials.description"
89+
v-for="section in page.sections"
90+
:key="section.id"
91+
:title="section.title"
92+
:description="section.description"
93+
:orientation="section.orientation"
94+
:reverse="section.reverse"
95+
:features="section.features"
96+
:ui="{
97+
title: 'text-3xl sm:text-4xl',
98+
description: 'text-lg text-gray-600 dark:text-gray-300'
99+
}"
68100
>
69-
<UPageColumns class="xl:columns-4">
70-
<UPageCard
71-
v-for="(testimonial, index) in page.testimonials.items"
72-
:key="index"
73-
variant="subtle"
74-
:description="testimonial.quote"
75-
:ui="{ description: 'before:content-[open-quote] after:content-[close-quote]' }"
76-
>
77-
<template #footer>
78-
<UUser
79-
v-bind="testimonial.user"
80-
size="lg"
81-
/>
82-
</template>
83-
</UPageCard>
84-
</UPageColumns>
101+
<NuxtLink v-if="section.image?.src" :to="section.image.to" :target="section.image.target" class="block">
102+
<NuxtImg
103+
:src="section.image.src"
104+
:alt="section.image.alt"
105+
class="mx-auto max-w-[320px] sm:max-w-[420px] md:max-w-[500px]"
106+
loading="lazy"
107+
/>
108+
</NuxtLink>
85109
</UPageSection>
86110

87-
<USeparator />
88-
89-
<UPageCTA
90-
v-bind="page.cta"
91-
variant="naked"
92-
class="overflow-hidden"
93-
>
94-
<LazyStarsBg />
95-
</UPageCTA>
111+
<UPageSection v-if="page.cta">
112+
<UPageCTA
113+
:title="page.cta.title"
114+
:description="page.cta.description"
115+
:links="page.cta.links"
116+
variant="subtle"
117+
:ui="{
118+
title: 'text-3xl sm:text-4xl',
119+
description: 'text-lg text-muted'
120+
}"
121+
/>
122+
</UPageSection>
96123
</div>
97124
</template>

content.config.ts

Lines changed: 41 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -38,37 +38,58 @@ export const collections = {
3838
type: 'page',
3939
schema: z.object({
4040
hero: z.object(({
41+
title: z.string().optional(),
42+
description: z.string().optional(),
4143
links: z.array(createLinkSchema())
4244
})),
45+
roles: z.object({
46+
title: z.string().nonempty(),
47+
description: z.string().nonempty(),
48+
items: z.array(
49+
z.object({
50+
title: z.string().nonempty(),
51+
description: z.string().nonempty(),
52+
icon: z.string().nonempty().editor({ input: 'icon' }),
53+
to: z.string().optional(),
54+
target: z.string().optional()
55+
})
56+
)
57+
}).optional(),
58+
socials: z.object({
59+
title: z.string().nonempty(),
60+
links: z.array(
61+
z.object({
62+
icon: z.string().nonempty().editor({ input: 'icon' }),
63+
to: z.string().nonempty(),
64+
label: z.string().nonempty()
65+
})
66+
)
67+
}).optional(),
4368
sections: z.array(
44-
createBaseSchema().extend({
69+
z.object({
70+
title: z.string().nonempty(),
71+
description: z.string().nonempty(),
4572
id: z.string().nonempty(),
4673
orientation: orientationEnum.optional(),
4774
reverse: z.boolean().optional(),
48-
features: z.array(createFeatureItemSchema())
49-
})
50-
),
51-
features: createBaseSchema().extend({
52-
items: z.array(createFeatureItemSchema())
53-
}),
54-
testimonials: createBaseSchema().extend({
55-
headline: z.string().optional(),
56-
items: z.array(
57-
z.object({
58-
quote: z.string().nonempty(),
59-
user: z.object({
75+
image: z.object({
76+
src: z.string().nonempty().editor({ input: 'media' }),
77+
alt: z.string().optional(),
78+
to: z.string().optional(),
79+
target: z.string().optional()
80+
}).optional(),
81+
features: z.array(
82+
z.object({
6083
name: z.string().nonempty(),
6184
description: z.string().nonempty(),
62-
to: z.string().nonempty(),
63-
target: z.string().nonempty(),
64-
avatar: createImageSchema()
85+
icon: z.string().nonempty().editor({ input: 'icon' })
6586
})
66-
})
67-
)
68-
}),
87+
).optional()
88+
})
89+
),
6990
cta: createBaseSchema().extend({
7091
links: z.array(createLinkSchema())
71-
})
92+
}).optional()
7293
})
7394
}),
7495
blog: defineCollection({

0 commit comments

Comments
 (0)