kayjaydee
|
e17faae5d7
|
feat(07-02): enrich blog article page with full SEO meta + Article/Breadcrumb JSON-LD
- D-15: useSeoMeta extended with ogImage (absolute via resolveOgImage),
ogUrl (canonical), ogLocale + ogLocaleAlternate (emitted only when bilingual
pair exists), twitterCard + twitterImage, article:published_time,
article:modified_time (fallback to date when updated absent — D-13),
articleAuthor
- SEO-11/SEO-15: useSchemaOrg([defineArticle, defineBreadcrumb])
— Article author/publisher reference global Person via @id=#killian
(from app/utils/seo-person.ts KILLIAN_PERSON_ID), image mirrors ogImage,
mainEntityOfPage = canonical; BreadcrumbList emits Accueil → Blog → title
- Pitfall 7: altExists query via queryCollection('blog_en'|'blog_fr') with
literal collection names (Vite extractor constraint)
- inLanguageTag computed cast to satisfy overly narrow defineArticle typings
without changing runtime emission
- Validated SSR: curl /fr/blog/test-kotlin-syntax returns og:image absolute,
article:published_time, Article JSON-LD (author @id=#killian), BreadcrumbList 3 items
|
2026-04-22 11:19:58 +02:00 |
|
kayjaydee
|
47c2839ae8
|
feat(07-03): enrich blog listing with D-16 useSeoMeta + CollectionPage/Breadcrumb JSON-LD
- Add SITE_URL + OG_FALLBACK constants (fallback hardcoded, resolveOgImage helper owned by 07-02)
- Extend useSeoMeta: ogImage (absolute /og-blog-default.jpg), ogUrl, ogLocale, ogLocaleAlternate, twitterCard, twitterImage
- Add useSchemaOrg([defineWebPage CollectionPage, defineBreadcrumb(Accueil -> Blog)])
- inLanguage resolved at setup (type constraint: literal union, not ComputedRef)
- Requirements: SEO-10, SEO-13, SEO-15
|
2026-04-22 11:17:10 +02:00 |
|
kayjaydee
|
f18b0bff2c
|
feat(06-04): enrich blog article page with breadcrumb, TOC, prev/next
- isFr converti en computed (fix Phase 5 non-reactive isFr)
- { watch: [locale] } sur les 2 useAsyncData (article + surround)
- queryCollectionItemSurroundings avec littéraux 'blog_fr'/'blog_en', fields explicites
- Article query WITHOUT draft filter (direct URL access, D-14)
- Surround query WITH .where('draft','=',false).order('date','DESC')
- Mapping prev=surround[1], next=surround[0] (Pitfall 4 DESC order)
- Header: UBreadcrumb + H1 + meta row (date Intl + reading time) + tags + cover NuxtImg eager
- Layout grid desktop [1fr_16rem] avec max-w-3xl colonne article
- ContentRenderer prose wrapper Phase 5 préservé
- BlogToc aside + BlogPrevNext en bas
- ogType: 'article' (préparation Phase 7)
Requirements: BLOG-03, BLOG-06
|
2026-04-22 10:09:23 +02:00 |
|
kayjaydee
|
eca09e0c32
|
feat(06-03): add blog listing page /blog (hero + grid + empty state)
- Query bilingue queryCollection('blog_fr') / queryCollection('blog_en') literal branches (Phase 5 gotcha)
- .where('draft', '=', false).order('date', 'DESC') with { watch: [locale] }
- Hero pattern /projects.vue: slogan // blog + H1 gradient + 3 stats (articles/tags/languages)
- Grid 1/2/3 responsive cols using BlogCard default variant
- Empty state with UIcon book-open + UButton CTA to /contact
- useSeoMeta minimal (full SEO + JSON-LD reserved for Phase 7)
Requirements: BLOG-02, BLOG-06
|
2026-04-22 10:05:16 +02:00 |
|
kayjaydee
|
127db8b77a
|
feat(blog): add dynamic blog post rendering with i18n support and error handling in [slug].vue
|
2026-04-22 00:20:52 +02:00 |
|
kayjaydee
|
277b407361
|
feat(05): i18n strategy prefix — /fr/blog and /en/blog explicit routes, update collection prefixes
|
2026-04-21 16:49:32 +02:00 |
|
kayjaydee
|
06f47cbe11
|
fix(05): blog EN path uses /en/blog prefix to match blog_en collection
|
2026-04-21 16:47:12 +02:00 |
|
kayjaydee
|
f2e29e6c2f
|
feat(05): add blog/[...slug].vue — render @nuxt/content articles via queryCollection
|
2026-04-21 16:45:34 +02:00 |
|