feat: Rearchitect personal website with new pages, blog functionality using Nuxt UI and Nuxt Content.

This commit is contained in:
2026-02-13 21:48:43 +03:00
parent f5b2f7a867
commit d45b2793f1
19 changed files with 746 additions and 115 deletions
+8
View File
@@ -0,0 +1,8 @@
{
"files.associations": {
"*.css": "tailwindcss"
},
"editor.quickSuggestions": {
"strings": true
}
}
+103 -2
View File
@@ -1,4 +1,105 @@
<script setup lang="ts">
import type { NavigationMenuItem } from "@nuxt/ui";
const route = useRoute();
const navItems = computed<NavigationMenuItem[]>(() => [
{
label: "About",
to: "/about",
active: route.path === "/about",
},
{
label: "Blog",
to: "/blog",
active: route.path.startsWith("/blog"),
},
{
label: "Projects",
to: "/#projects",
active: false,
},
{
label: "Contact",
to: "/#contact",
active: false,
},
]);
</script>
<template> <template>
<NuxtRouteAnnouncer /> <UApp>
<NuxtPage /> <UHeader>
<template #title>
<NuxtLink to="/" class="flex items-center gap-2">
<span class="inline-flex items-center justify-center size-8 rounded-full bg-primary-600 text-white text-sm font-bold">IW</span>
<div class="flex flex-col leading-tight">
<span class="font-semibold text-sm">Ian Wati</span>
<span class="text-xs text-(--ui-text-muted)">Software Engineer</span>
</div>
</NuxtLink>
</template>
<UNavigationMenu :items="navItems" />
<template #right>
<UButton
color="neutral"
variant="ghost"
to="https://github.com/andwati"
target="_blank"
icon="i-simple-icons-github"
aria-label="GitHub"
/>
<UButton
color="neutral"
variant="ghost"
to="https://x.com"
target="_blank"
icon="i-simple-icons-x"
aria-label="X"
/>
<UColorModeButton />
</template>
<template #body>
<UNavigationMenu
:items="navItems"
orientation="vertical"
class="-mx-2.5"
/>
</template>
</UHeader>
<UMain>
<NuxtPage />
</UMain>
<UFooter>
<template #left>
<p class="text-sm text-(--ui-text-muted)">
© {{ new Date().getFullYear() }} Ian Wati. Built with Nuxt.
</p>
</template>
<template #right>
<UButton
color="neutral"
variant="ghost"
to="https://github.com/andwati"
target="_blank"
icon="i-simple-icons-github"
aria-label="GitHub"
/>
<UButton
color="neutral"
variant="ghost"
to="https://x.com"
target="_blank"
icon="i-simple-icons-x"
aria-label="X"
/>
</template>
</UFooter>
</UApp>
</template> </template>
+18
View File
@@ -0,0 +1,18 @@
@import "tailwindcss";
@import "@nuxt/ui";
@source "../../../content/**/*";
@theme {
--color-primary-50: #f0fdf4;
--color-primary-100: #dcfce7;
--color-primary-200: #bbf7d0;
--color-primary-300: #86efac;
--color-primary-400: #4ade80;
--color-primary-500: #22c55e;
--color-primary-600: #16a34a;
--color-primary-700: #15803d;
--color-primary-800: #166534;
--color-primary-900: #14532d;
--color-primary-950: #052e16;
}
-32
View File
@@ -1,32 +0,0 @@
<template>
<div class="alert" :style="{ 'border-color': color }">
<slot></slot>
</div>
</template>
<script setup lang="ts">
const { color } = defineProps({
color: {
type: String,
default: "orange",
},
});
</script>
<style scoped>
.alert {
display: flex;
align-items: center;
padding: 1rem;
border: 2px solid;
border-radius: 0.5rem;
background-color: #fff;
box-shadow: 0 4px 6px rgba(0, 0, 0, 0.1);
transition: transform 0.2s ease, box-shadow 0.2s ease;
}
.alert:hover {
transform: translateY(-2px);
box-shadow: 0 6px 8px rgba(0, 0, 0, 0.15);
}
</style>
-29
View File
@@ -1,29 +0,0 @@
<template>
<div>
<h3>Counter: {{ count }}</h3>
<button @click="increment">
Increment
</button>
<button @click="decrement">
Decrement
</button>
</div>
</template>
<script setup lang="ts">
const count = ref(0);
const increment = () => {
count.value++;
};
const decrement = () => {
count.value--;
};
</script>
<style scoped>
button {
margin: 5px;
}
</style>
-22
View File
@@ -1,22 +0,0 @@
<script setup lang="ts">
const route = useRoute();
const { data: page } = await useAsyncData("page-" + route.path, () => {
return queryCollection("content").path(route.path).first();
});
if (!page.value) {
throw createError({
statusCode: 404,
statusMessage: "Page not found",
fatal: true,
});
}
</script>
<template>
<ContentRenderer
v-if="page"
:value="page"
/>
</template>
+116
View File
@@ -0,0 +1,116 @@
<script setup lang="ts">
useSeoMeta({
title: "About — Ian Wati",
description:
"Learn more about Ian Wati — software engineer passionate about building great products.",
});
const skills = [
{ name: "TypeScript", icon: "i-simple-icons-typescript" },
{ name: "Vue.js", icon: "i-simple-icons-vuedotjs" },
{ name: "Nuxt", icon: "i-simple-icons-nuxtdotjs" },
{ name: "Node.js", icon: "i-simple-icons-nodedotjs" },
{ name: "Go", icon: "i-simple-icons-go" },
{ name: "Python", icon: "i-simple-icons-python" },
{ name: "Docker", icon: "i-simple-icons-docker" },
{ name: "Git", icon: "i-simple-icons-git" },
{ name: "PostgreSQL", icon: "i-simple-icons-postgresql" },
];
</script>
<template>
<div>
<!-- Hero -->
<section class="relative overflow-hidden">
<div class="absolute inset-0 bg-gradient-to-br from-primary-50 via-white to-primary-100/50 dark:from-primary-950/20 dark:via-(--ui-bg) dark:to-primary-900/10" />
<UContainer class="relative py-16 lg:py-24">
<div class="flex flex-col sm:flex-row items-center sm:items-start gap-8 max-w-3xl">
<img
src="https://avatars.githubusercontent.com/u/andwati"
alt="Ian Wati"
class="rounded-full size-32 ring-4 ring-primary-200 dark:ring-primary-800 shadow-lg flex-shrink-0"
/>
<div class="space-y-4 text-center sm:text-left">
<p class="text-xs font-semibold tracking-[0.2em] uppercase text-(--ui-text-muted)">About</p>
<h1 class="text-3xl sm:text-4xl font-bold">Ian Wati</h1>
<p class="text-lg text-(--ui-text-muted) leading-relaxed">
I'm a software engineer who loves building tools and products that make a difference.
My interests span full-stack development, open source, and exploring new technologies.
</p>
</div>
</div>
</UContainer>
</section>
<!-- What I Do -->
<section class="py-16 lg:py-24">
<UContainer>
<div class="max-w-3xl mx-auto space-y-12">
<div>
<p class="text-xs font-semibold tracking-[0.2em] uppercase text-primary-600 dark:text-primary-400 mb-3">Focus</p>
<h2 class="text-2xl font-bold mb-4">What I Do</h2>
<p class="text-(--ui-text-muted) leading-relaxed">
I specialize in building modern web applications using cutting-edge technologies.
Whether it's crafting a seamless user experience on the frontend or architecting
robust backend systems, I enjoy the entire spectrum of software development.
</p>
</div>
<USeparator />
<!-- Skills -->
<div>
<p class="text-xs font-semibold tracking-[0.2em] uppercase text-primary-600 dark:text-primary-400 mb-3">Skills</p>
<h2 class="text-2xl font-bold mb-6">Technologies</h2>
<div class="grid grid-cols-3 sm:grid-cols-4 lg:grid-cols-5 gap-4">
<div
v-for="skill in skills"
:key="skill.name"
class="flex flex-col items-center gap-2 p-4 rounded-xl bg-(--ui-bg-elevated) border border-(--ui-border) hover:border-primary-300 dark:hover:border-primary-700 transition-colors"
>
<UIcon :name="skill.icon" class="size-8 text-primary-600 dark:text-primary-400" />
<span class="text-xs font-medium text-(--ui-text-muted)">{{ skill.name }}</span>
</div>
</div>
</div>
<USeparator />
<!-- Contact -->
<div>
<p class="text-xs font-semibold tracking-[0.2em] uppercase text-primary-600 dark:text-primary-400 mb-3">Connect</p>
<h2 class="text-2xl font-bold mb-4">Get in Touch</h2>
<p class="text-(--ui-text-muted) leading-relaxed mb-6">
Feel free to reach out through any of the links below. Always happy to
connect and discuss interesting projects or ideas.
</p>
<div class="flex flex-wrap gap-3">
<UButton
label="GitHub"
to="https://github.com/andwati"
target="_blank"
icon="i-simple-icons-github"
color="neutral"
variant="outline"
/>
<UButton
label="X / Twitter"
to="https://x.com"
target="_blank"
icon="i-simple-icons-x"
color="neutral"
variant="outline"
/>
<UButton
label="Email me"
to="mailto:[email protected]"
icon="i-lucide-mail"
/>
</div>
</div>
</div>
</UContainer>
</section>
</div>
</template>
+53
View File
@@ -0,0 +1,53 @@
<script setup lang="ts">
const route = useRoute();
const { data: post } = await useAsyncData("blog-post-" + route.path, () =>
queryCollection("blog").path(route.path).first(),
);
if (!post.value) {
throw createError({
statusCode: 404,
statusMessage: "Blog post not found",
fatal: true,
});
}
useSeoMeta({
title: post.value?.title,
description: post.value?.description,
});
</script>
<template>
<UPage v-if="post">
<UPageHeader
:title="post.title"
:description="post.description"
>
<template #headline>
<UBreadcrumb
:items="[
{ label: 'Blog', to: '/blog', icon: 'i-lucide-book-open' },
{ label: post.title || '' }
]"
/>
</template>
<template #bottom>
<div class="flex items-center gap-3 text-sm text-(--ui-text-muted)">
<UIcon name="i-lucide-calendar" />
<time v-if="post.date">{{ new Date(post.date).toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: 'numeric' }) }}</time>
</div>
</template>
</UPageHeader>
<UPageBody>
<UContainer>
<div class="prose prose-primary dark:prose-invert max-w-none">
<ContentRenderer :value="post" />
</div>
</UContainer>
</UPageBody>
</UPage>
</template>
+43
View File
@@ -0,0 +1,43 @@
<script setup lang="ts">
const { data: posts } = await useAsyncData("blog-posts", () =>
queryCollection("blog").order("date", "DESC").all(),
);
useSeoMeta({
title: "Blog — Ian Wati",
description:
"Thoughts on software development, technology, and the things I learn along the way.",
});
</script>
<template>
<UPage>
<UPageHero
title="Blog"
description="Thoughts on software development, technology, and the things I learn along the way."
/>
<UPageBody>
<UContainer>
<UBlogPosts v-if="posts?.length">
<UBlogPost
v-for="post in posts"
:key="post.path"
:title="post.title"
:description="post.description"
:date="post.date"
:image="post.image"
:badge="post.badge"
:to="post.path"
/>
</UBlogPosts>
<div v-else class="text-center py-12">
<p class="text-(--ui-text-muted)">
No blog posts yet. Check back soon!
</p>
</div>
</UContainer>
</UPageBody>
</UPage>
</template>
+246
View File
@@ -0,0 +1,246 @@
<script setup lang="ts">
const { data: recentPosts } = await useAsyncData("recent-posts", () =>
queryCollection("blog").order("date", "DESC").limit(3).all(),
);
useSeoMeta({
title: "Ian Wati — Software Engineer",
description:
"Software engineer passionate about building great products. I write about development, technology, and the things I learn along the way.",
});
const codeSnippet = `const softwareEngineer = {
name: 'Ian Wati',
role: 'Software Engineer',
skills: [
'TypeScript', 'Vue.js', 'Nuxt',
'Node.js', 'Go', 'Python'
],
getProjects: async () => {
return await fetchProjects();
},
};`;
</script>
<template>
<div>
<!-- Hero Section -->
<section class="relative overflow-hidden">
<div class="absolute inset-0 bg-gradient-to-br from-primary-50 via-white to-primary-100/50 dark:from-primary-950/20 dark:via-(--ui-bg) dark:to-primary-900/10" />
<UContainer class="relative py-16 lg:py-24">
<div class="grid lg:grid-cols-2 gap-12 lg:gap-16 items-center">
<!-- Left: Text -->
<div class="space-y-6">
<p class="text-xs font-semibold tracking-[0.2em] uppercase text-(--ui-text-muted)">
Software Engineer
</p>
<h1 class="text-4xl sm:text-5xl lg:text-6xl font-bold leading-tight">
Hi, I'm <span class="text-primary-600 dark:text-primary-400">Ian Wati</span>
</h1>
<p class="text-lg sm:text-xl text-(--ui-text-muted) leading-relaxed max-w-xl">
I build modern web applications and love crafting software that makes a real impact.
Passionate about clean code, open source, and continuous learning.
</p>
<div class="flex flex-wrap gap-3 pt-2">
<UButton
label="Read the Blog"
to="/blog"
icon="i-lucide-book-open"
size="xl"
/>
<UButton
label="Get in Touch"
to="#contact"
color="neutral"
variant="outline"
icon="i-lucide-mail"
size="xl"
/>
</div>
<div class="flex items-center gap-4 pt-2 text-sm text-(--ui-text-muted)">
<span class="flex items-center gap-1.5">
<UIcon name="i-lucide-map-pin" class="size-4" />
Nairobi, Kenya
</span>
<span class="flex items-center gap-1.5">
<UIcon name="i-lucide-mail" class="size-4" />
hello@ianwati.dev
</span>
</div>
</div>
<!-- Right: Code Editor -->
<div class="relative hidden lg:block">
<!-- Floating stat cards -->
<div class="absolute -top-4 -right-2 z-10">
<div class="bg-primary-800 text-white rounded-xl px-4 py-3 shadow-lg flex items-center gap-3">
<UIcon name="i-lucide-git-commit-horizontal" class="size-5 text-primary-300" />
<div>
<p class="font-bold text-sm">Open Source</p>
<p class="text-xs text-primary-200">Active Contributor</p>
</div>
</div>
</div>
<div class="absolute -bottom-2 -right-4 z-10">
<div class="bg-primary-700 text-white rounded-xl px-4 py-3 shadow-lg flex items-center gap-3">
<UIcon name="i-lucide-code-2" class="size-5 text-primary-300" />
<div>
<p class="font-bold text-sm">Full Stack</p>
<p class="text-xs text-primary-200">Frontend & Backend</p>
</div>
</div>
</div>
<!-- Code window -->
<div class="bg-primary-950 dark:bg-gray-900 rounded-xl shadow-2xl overflow-hidden ring ring-primary-800/20">
<div class="flex items-center gap-2 px-4 py-3 border-b border-white/10">
<div class="flex gap-1.5">
<span class="size-3 rounded-full bg-red-500" />
<span class="size-3 rounded-full bg-yellow-500" />
<span class="size-3 rounded-full bg-green-500" />
</div>
<span class="text-xs text-white/40 ml-2">portfolio.ts</span>
</div>
<pre class="p-5 text-sm font-mono leading-relaxed overflow-x-auto"><code class="text-green-400">{{ codeSnippet }}</code></pre>
<div class="flex items-center justify-between px-4 py-2 border-t border-white/10 text-xs text-white/40">
<span class="flex items-center gap-1.5">
<span class="size-2 rounded-full bg-green-500" />
Online and coding
</span>
<span>Last commit: Today</span>
</div>
</div>
</div>
</div>
</UContainer>
</section>
<!-- About Section -->
<section id="about" class="py-16 lg:py-24">
<UContainer>
<div class="text-center mb-12">
<p class="text-xs font-semibold tracking-[0.2em] uppercase text-primary-600 dark:text-primary-400 mb-3">About</p>
<h2 class="text-3xl sm:text-4xl font-bold">What I Do</h2>
</div>
<div class="grid sm:grid-cols-2 lg:grid-cols-3 gap-6">
<UCard>
<div class="space-y-3">
<div class="inline-flex items-center justify-center size-10 rounded-lg bg-primary-100 dark:bg-primary-900/30">
<UIcon name="i-lucide-monitor" class="size-5 text-primary-600 dark:text-primary-400" />
</div>
<h3 class="font-semibold text-lg">Frontend Development</h3>
<p class="text-sm text-(--ui-text-muted) leading-relaxed">Building responsive, accessible, and performant user interfaces with Vue.js, Nuxt, and modern CSS.</p>
</div>
</UCard>
<UCard>
<div class="space-y-3">
<div class="inline-flex items-center justify-center size-10 rounded-lg bg-primary-100 dark:bg-primary-900/30">
<UIcon name="i-lucide-server" class="size-5 text-primary-600 dark:text-primary-400" />
</div>
<h3 class="font-semibold text-lg">Backend Development</h3>
<p class="text-sm text-(--ui-text-muted) leading-relaxed">Designing robust APIs and services with Node.js, Go, and Python. Focused on scalability and reliability.</p>
</div>
</UCard>
<UCard>
<div class="space-y-3">
<div class="inline-flex items-center justify-center size-10 rounded-lg bg-primary-100 dark:bg-primary-900/30">
<UIcon name="i-lucide-lightbulb" class="size-5 text-primary-600 dark:text-primary-400" />
</div>
<h3 class="font-semibold text-lg">Open Source</h3>
<p class="text-sm text-(--ui-text-muted) leading-relaxed">Contributing to open source projects and building tools that help the developer community grow.</p>
</div>
</UCard>
</div>
</UContainer>
</section>
<!-- Recent Blog Posts Section -->
<section class="py-16 lg:py-24 bg-(--ui-bg-elevated)">
<UContainer>
<div class="flex items-end justify-between mb-12">
<div>
<p class="text-xs font-semibold tracking-[0.2em] uppercase text-primary-600 dark:text-primary-400 mb-3">Blog</p>
<h2 class="text-3xl sm:text-4xl font-bold">Latest Posts</h2>
<p class="mt-2 text-(--ui-text-muted)">Thoughts on development, technology, and things I learn along the way.</p>
</div>
<UButton
label="View All"
to="/blog"
variant="ghost"
trailing-icon="i-lucide-arrow-right"
class="hidden sm:inline-flex"
/>
</div>
<UBlogPosts v-if="recentPosts?.length">
<UBlogPost
v-for="post in recentPosts"
:key="post.path"
:title="post.title"
:description="post.description"
:date="post.date"
:image="post.image"
:badge="post.badge"
:to="post.path"
/>
</UBlogPosts>
<div v-else class="text-center py-12">
<p class="text-(--ui-text-muted)">No blog posts yet. Stay tuned!</p>
</div>
<div class="mt-8 text-center sm:hidden">
<UButton label="View All Posts" to="/blog" variant="subtle" trailing-icon="i-lucide-arrow-right" />
</div>
</UContainer>
</section>
<!-- Contact Section -->
<section id="contact" class="py-16 lg:py-24">
<UContainer>
<div class="max-w-2xl mx-auto text-center space-y-6">
<p class="text-xs font-semibold tracking-[0.2em] uppercase text-primary-600 dark:text-primary-400">Contact</p>
<h2 class="text-3xl sm:text-4xl font-bold">Get in Touch</h2>
<p class="text-lg text-(--ui-text-muted)">
Have a project in mind or just want to connect? Feel free to reach out.
</p>
<div class="flex flex-wrap justify-center gap-4 pt-4">
<UButton
label="Email me"
to="mailto:[email protected]"
icon="i-lucide-mail"
size="xl"
/>
<UButton
label="GitHub"
to="https://github.com/andwati"
target="_blank"
icon="i-simple-icons-github"
color="neutral"
variant="outline"
size="xl"
/>
<UButton
label="X / Twitter"
to="https://x.com"
target="_blank"
icon="i-simple-icons-x"
color="neutral"
variant="outline"
size="xl"
/>
</div>
</div>
</UContainer>
</section>
</div>
</template>
+10 -1
View File
@@ -1,4 +1,4 @@
import { defineContentConfig, defineCollection } from "@nuxt/content"; import { defineContentConfig, defineCollection, z } from "@nuxt/content";
export default defineContentConfig({ export default defineContentConfig({
collections: { collections: {
@@ -6,5 +6,14 @@ export default defineContentConfig({
type: "page", type: "page",
source: "**", source: "**",
}), }),
blog: defineCollection({
type: "page",
source: "blog/**",
schema: z.object({
date: z.string(),
image: z.string().optional(),
badge: z.string().optional(),
}),
}),
}, },
}); });
-3
View File
@@ -1,3 +0,0 @@
# About Content Version 3
[Back home](/)
@@ -0,0 +1,56 @@
---
title: "Building a Blog with Nuxt Content"
description: "Learn how to use Nuxt Content v3 to create a powerful, file-based blog with markdown support."
date: "2025-02-01"
image: "https://images.unsplash.com/photo-1499750310107-5fef28a66643?w=800&h=400&fit=crop"
badge: "Tutorial"
---
# Building a Blog with Nuxt Content
Nuxt Content v3 is a powerful module that turns your Nuxt application into a git-based CMS. In this post, we'll explore how to set up a blog using Nuxt Content.
## Why Nuxt Content?
Nuxt Content provides several advantages for content management:
- **Markdown support** — Write your content in Markdown with full MDC syntax support
- **Collections** — Organize your content into typed collections with schema validation
- **Querying** — Powerful query API to filter, sort, and paginate your content
- **Vue components in Markdown** — Embed Vue components directly in your markdown files
## Setting Up Collections
Content collections are defined in your `content.config.ts` file:
```typescript
import { defineContentConfig, defineCollection, z } from "@nuxt/content";
export default defineContentConfig({
collections: {
blog: defineCollection({
type: "page",
source: "blog/**",
schema: z.object({
date: z.string(),
image: z.string().optional(),
badge: z.string().optional(),
}),
}),
},
});
```
## Querying Content
You can query your blog posts using the `queryCollection` composable:
```typescript
const { data: posts } = await useAsyncData("posts", () =>
queryCollection("blog").order("date", "DESC").all()
);
```
## Conclusion
Nuxt Content makes it incredibly easy to build a content-driven website. With collections, schema validation, and the powerful query API, you have all the tools you need to create a great blog experience.
@@ -0,0 +1,45 @@
---
title: "Getting Started with Nuxt 4"
description: "An introduction to Nuxt 4 and its new features that make web development even more enjoyable."
date: "2025-01-15"
image: "https://images.unsplash.com/photo-1555066931-4365d14bab8c?w=800&h=400&fit=crop"
badge: "Nuxt"
---
# Getting Started with Nuxt 4
Nuxt 4 represents a significant evolution in the Nuxt ecosystem. With a refined project structure, improved developer experience, and powerful new features, it's never been a better time to start building with Nuxt.
## What's New in Nuxt 4
Nuxt 4 brings several exciting improvements:
- **New directory structure** — The `app/` directory is now the default home for your Vue application code
- **Improved TypeScript support** — Better type inference and auto-completion out of the box
- **Enhanced performance** — Faster builds and smaller bundle sizes
- **Better data fetching** — Reworked async data layer for more predictable behavior
## Getting Started
Setting up a new Nuxt 4 project is straightforward:
```bash
npx nuxi@latest init my-app
```
This creates a fresh project with all the latest defaults. From there, you can start building your application right away.
## Why Nuxt?
Nuxt takes the best of Vue.js and adds powerful conventions and features on top:
1. **File-based routing** — No manual route configuration needed
2. **Auto-imports** — Components, composables, and utilities are automatically available
3. **Server-side rendering** — Built-in SSR for better performance and SEO
4. **Modules ecosystem** — Rich ecosystem of modules for common needs
Whether you're building a simple landing page or a complex full-stack application, Nuxt provides the tools and conventions to do it efficiently.
## Conclusion
Nuxt 4 is a fantastic framework for modern web development. Give it a try and see how it can streamline your workflow!
+41
View File
@@ -0,0 +1,41 @@
---
title: "Why I Love Open Source"
description: "Reflections on the open source community and why contributing to open source projects is so rewarding."
date: "2025-02-10"
image: "https://images.unsplash.com/photo-1522071820081-009f0129c71c?w=800&h=400&fit=crop"
badge: "Thoughts"
---
# Why I Love Open Source
Open source software has transformed the way we build technology. As a developer, contributing to open source projects has been one of the most rewarding aspects of my career.
## The Power of Community
The open source community is incredibly diverse and welcoming. Some of the best learning experiences come from:
- **Code reviews** — Getting feedback from experienced developers
- **Issue discussions** — Understanding real-world problems and solutions
- **Collaboration** — Working with people from around the world
## Giving Back
Contributing to open source isn't just about writing code. There are many ways to help:
1. **Documentation** — Good docs make projects accessible to everyone
2. **Bug reports** — Detailed bug reports save maintainers hours of work
3. **Testing** — Trying out new features and providing feedback
4. **Mentoring** — Helping newcomers get started with contributions
## Building in the Open
One of the things I enjoy most is building projects in the open. Sharing your work publicly:
- Forces you to write cleaner, more maintainable code
- Creates a portfolio of your work
- Helps others learn from your approach
- Opens doors to collaboration and feedback
## Final Thoughts
If you haven't contributed to open source yet, I encourage you to start. Find a project you use and love, and look for ways to help. Every contribution, no matter how small, makes a difference.
-24
View File
@@ -1,24 +0,0 @@
# Welcome to Nuxt Content Starter
This is the main page displaying Markdown located at [content/index.md](https://github.com/nuxt/starter/blob/content/content/index.md).
Move to [about](/about) page.
## Manage your Contents
Create new pages or modify the existing ones in `content/` directory.
## Query & Render Pages
You can find an example of querying contents and rendering them in a [catch-all page](https://github.com/nuxt/starter/blob/content/app/pages/%5B...slug%5D.vue)
## Integrate Vue Component
::alert{color="green"}
The current [alert](https://github.com/nuxt/starter/blob/content/app/components/Alert.vue) and the [counter](https://github.com/nuxt/starter/blob/content/app/components/Counter.vue) below are `Vue` components integrated into the Markdown.
::
::counter
::
Checkout out the [documentation](https://content.nuxt.com/docs/getting-started) to learn more.
+2 -1
View File
@@ -1,6 +1,7 @@
// https://nuxt.com/docs/api/configuration/nuxt-config // https://nuxt.com/docs/api/configuration/nuxt-config
export default defineNuxtConfig({ export default defineNuxtConfig({
modules: ["@nuxt/content"], modules: ["@nuxt/ui", "@nuxt/image", "@nuxt/content"],
devtools: { enabled: true }, devtools: { enabled: true },
css: ["~/assets/css/main.css"],
compatibilityDate: "2024-04-03", compatibilityDate: "2024-04-03",
}); });
+2 -1
View File
@@ -14,7 +14,8 @@
"@nuxt/image": "2.0.0", "@nuxt/image": "2.0.0",
"@nuxt/ui": "4.4.0", "@nuxt/ui": "4.4.0",
"better-sqlite3": "^12.6.2", "better-sqlite3": "^12.6.2",
"nuxt": "^4.3.1" "nuxt": "^4.3.1",
"tailwindcss": "^4.1.18"
}, },
"devDependencies": { "devDependencies": {
"@biomejs/biome": "2.3.15" "@biomejs/biome": "2.3.15"