Skip to content

feat: implement talks pages with content and UI#15

Merged
toddeTV merged 13 commits into
mainfrom
feat/implement-talks-pages-and-data
Mar 7, 2026
Merged

feat: implement talks pages with content and UI#15
toddeTV merged 13 commits into
mainfrom
feat/implement-talks-pages-and-data

Conversation

@toddeTV
Copy link
Copy Markdown
Owner

@toddeTV toddeTV commented Mar 7, 2026

This PR adds a talk overview and detail talk subpages with talk content collection and data for speaking engagements.

Summary by CodeRabbit

  • New Features

    • Added a talks section with listing and individual talk pages, plus new talk content entries
    • New testimonial carousel and card components for browsing feedback
  • Updates

    • Enhanced Open Graph image templates with new ambient glow, avatar, and footer visuals
    • Switched avatar asset from .webp to .jpg and adjusted avatar styling across previews and pages

@toddeTV toddeTV self-assigned this Mar 7, 2026
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented Mar 7, 2026

📝 Walkthrough

Walkthrough

Adds a Talks feature: content schemas and 15 talk markdown files, new pages and UI components (talk list, talk detail, TalkCard, testimonials carousel), OG image components (avatar, glow, footer, talk OG), switches avatar refs from .webp to .jpg, and adds duplicated Satori rendering instructions in .coderabbit.yml.

Changes

Cohort / File(s) Summary
CI / Instructions
\.coderabbit\.yml
Added two identical Satori/OG rendering instruction blocks targeting app/components/OgImage/**\/*.vue (inline styles only, no Tailwind/style blocks, hardcoded hex colors, specific DOM requirements).
Image Asset Format
README.md, nuxt.config.ts, app/components/OgImage/...
Switched avatar asset references from .webp to .jpg across README, nuxt config, and OG image components.
Content Schema & Config
content.config.ts
Added talks collection and internal testimonialSchema; changed content collection source to pages/** with prefix: '/'.
Talk Content
content/talks/*
Added 15 new talk markdown files (2022–2026) with frontmatter metadata (date, event, location, optional URLs, optional testimonials).
OG Image Components
app/components/OgImage/OgImageHome.vue, app/components/OgImage/OgImageTalk.vue, app/components/OgImage/components/OgImageGlow.vue, app/components/OgImage/components/OgImageAvatar.vue, app/components/OgImage/components/OgImageFooter.vue
Added OgImageTalk.vue, new components OgImageGlow and OgImageAvatar, refactored OgImageHome to use glow/footer components, and restructured footer layout; styles are inline for Satori compatibility.
Content Display Components
app/components/ui/AppCard.vue, app/components/content/TalkCard.vue, app/components/content/TestimonialCard.vue, app/components/content/TestimonialCarousel.vue
Added TalkCard, TestimonialCard, and a drag-enabled TestimonialCarousel; consolidated AppCard classes and adjusted interactive styling.
Pages (Talks)
app/pages/talks/index.vue, app/pages/talks/[slug].vue
Added talks listing page (upcoming + past grouped by year) and dynamic talk detail page with SEO/OG metadata, action buttons, and optional testimonials carousel.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes


Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
app/components/OgImage/OgImageHome.vue (1)

80-104: 🧹 Nitpick | 🔵 Trivial

Extract this avatar block behind OgImageAvatar props instead of maintaining a second copy.

This now duplicates the same hardcoded source/alt and circular avatar wrapper pattern introduced in app/components/OgImage/components/OgImageAvatar.vue. The next asset or framing tweak will have to be updated in both places.

As per coding guidelines, "No duplication: extract repeated logic, markup patterns, or style combinations into reusable components, composables, or utilities. Refactor existing duplicates on sight."

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@app/components/OgImage/OgImageHome.vue` around lines 80 - 104, Replace the
duplicated avatar markup in OgImageHome.vue with the reusable OgImageAvatar
component: remove the hardcoded div/img block and render <OgImageAvatar> passing
the same image source and alt (src="/avatar-thorsten-seyschab.jpg",
alt="Thorsten Seyschab") and any size/position data as props (e.g., size or
diameter=350 and positioning props like top="115px" right="80px" or a
wrapperStyle prop) so the circular wrapper, gradient border and objectFit are
centralized in OgImageAvatar.vue; ensure the prop names you use match the
component's props in OgImageAvatar and update any imports/registration in
OgImageHome.vue.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/components/content/TalkCard.vue`:
- Around line 84-92: There are two identical CSS rules for .card-link::after in
TalkCard.vue; remove the duplicate block so only a single .card-link::after rule
remains (keep one instance with the existing declarations: `@apply` absolute
inset-0; content: '';), making sure to remove the second occurrence and run a
quick style check to confirm no other duplicates remain.

In `@app/components/OgImage/components/OgImageFooter.vue`:
- Around line 19-45: The outer container div (the one with inline styles for
display:flex and justifyContent: 'space-between') must include a class name that
contains "flex-" (e.g., "flex-row") so Satori's flex plugin doesn't force
flexDirection: 'column'; add that class to the container and change the two
inner block <div> text elements (the nodes rendering "Thorsten Seyschab" and
"todde.tv") to inline elements such as <span> to avoid triggering the
block-children rule while keeping their existing inline style objects.

In `@app/components/OgImage/OgImageTalk.vue`:
- Around line 17-29: The root div (the outermost container with inline style
width: '1200px', height: '630px', display: 'flex', ...) and the main content
container (the inner container under it that currently uses inline flex styles)
need a CSS class containing the substring "flex-" to prevent nuxt-og-image's
flex plugin from injecting unwanted flexWrap and gap styles; add a class like
"flex-root" on the outermost div and "flex-main" on the main content container
so both elements include a "flex-" prefixed class, leaving existing inline
styles intact.

In `@app/components/ui/AppCard.vue`:
- Line 46: The template applies a non-existent CSS class "app-card-interactive"
alongside "app-card-hover" and "app-card-base"; either add a matching rule for
.app-card-interactive to the component's <style scoped> block (e.g., styles for
interactive state, pointer cursor, focus/active outlines consistent with
.app-card-hover) or remove "app-card-interactive" from the class list in the
template so only defined classes (.app-card-base, .app-card-hover, .app-card)
are used; update the component accordingly (AppCard.vue — template class list
and/or style block).

In `@content.config.ts`:
- Around line 63-67: Change the URL fields in the talks schema to use the same
URL validator as the socials collection: replace the current repoUrl, slidesUrl,
and videoUrl definitions (currently using z.string().optional()) with the URL
validator used elsewhere (i.e., z.url().optional()) so they perform proper URL
format validation and remain consistent with the socials schema.
- Line 57: Replace the loose date validator z.string() on the schema's date
field with z.iso.date() so the 'date' field enforces ISO 8601 (YYYY-MM-DD)
date-only format; locate the schema definition where the property name 'date'
currently uses z.string() and change it to use z.iso.date(), ensuring imports
from Zod remain correct.

In `@content/talks/2024-09-17-pragvue.md`:
- Around line 1-9: Create a GitHub issue to track publishing the talk repository
referenced in content/talks/2024-09-17-pragvue.md: use a clear title like
"Publish PragVue talk repository for Moving Vue to 3D", include the commented
repo URL (https://github.com/toddeTV/talk_2024-09-17_PragVue-conference),
describe the TODO in the file and required next steps (make repo public, add
slides/code, add README and license), assign the owner and set a milestone/label
for publishing.

---

Outside diff comments:
In `@app/components/OgImage/OgImageHome.vue`:
- Around line 80-104: Replace the duplicated avatar markup in OgImageHome.vue
with the reusable OgImageAvatar component: remove the hardcoded div/img block
and render <OgImageAvatar> passing the same image source and alt
(src="/avatar-thorsten-seyschab.jpg", alt="Thorsten Seyschab") and any
size/position data as props (e.g., size or diameter=350 and positioning props
like top="115px" right="80px" or a wrapperStyle prop) so the circular wrapper,
gradient border and objectFit are centralized in OgImageAvatar.vue; ensure the
prop names you use match the component's props in OgImageAvatar and update any
imports/registration in OgImageHome.vue.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: ASSERTIVE

Plan: Lite

Run ID: 3bcec45c-7cc1-4525-b420-375dd3a5e559

📥 Commits

Reviewing files that changed from the base of the PR and between 338312b and 24a8922.

⛔ Files ignored due to path filters (1)
  • public/avatar-thorsten-seyschab.jpg is excluded by !**/*.jpg
📒 Files selected for processing (31)
  • .coderabbit.yml
  • README.md
  • app/components/OgImage/OgImageHome.vue
  • app/components/OgImage/OgImageTalk.vue
  • app/components/OgImage/components/OgImageAvatar.vue
  • app/components/OgImage/components/OgImageFooter.vue
  • app/components/OgImage/components/OgImageGlow.vue
  • app/components/content/TalkCard.vue
  • app/components/content/TestimonialCard.vue
  • app/components/content/TestimonialCarousel.vue
  • app/components/ui/AppCard.vue
  • app/pages/talks/[slug].vue
  • app/pages/talks/index.vue
  • content.config.ts
  • content/pages/example.md
  • content/talks/2022-10-28-musiktheorie-symposium.md
  • content/talks/2023-01-25-masters-thesis-defense.md
  • content/talks/2024-09-17-pragvue.md
  • content/talks/2024-11-12-nuxtnation.md
  • content/talks/2025-01-29-vue-js-nation.md
  • content/talks/2025-02-13-hamburg-vue-js-meetup.md
  • content/talks/2025-04-08-dev-day.md
  • content/talks/2025-05-22-printed-europe.md
  • content/talks/2025-06-04-frontend-nation.md
  • content/talks/2025-09-23-prag-vue.md
  • content/talks/2025-10-08-pre-vite-conf.md
  • content/talks/2025-10-10-vite-conf.md
  • content/talks/2025-10-25-vue-fes-japan.md
  • content/talks/2026-03-13-vuejs-amsterdam.md
  • nuxt.config.ts
  • public/avatar-thorsten-seyschab.webp

Comment thread app/components/content/TalkCard.vue Outdated
Comment thread app/components/OgImage/components/OgImageFooter.vue
Comment thread app/components/OgImage/OgImageTalk.vue
Comment thread app/components/ui/AppCard.vue Outdated
Comment thread content.config.ts Outdated
Comment thread content.config.ts Outdated
Comment thread content/talks/2024-09-17-pragvue.md
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/components/content/TalkCard.vue`:
- Around line 15-16: The isUpcoming computed currently uses useTodayDate()
inside TalkCard.vue (isUpcoming = computed(() => props.talk.date >=
today.value)), which bakes a build-time "today" into generated HTML; remove the
runtime useTodayDate dependency and instead derive the upcoming flag at build
time or from persisted metadata: either add an isUpcoming boolean to the talk
data/frontmatter (props.talk.isUpcoming) or accept a build-time
buildDate/buildTimestamp injected by the page and compute isUpcoming in the page
(not inside TalkCard) and pass the resulting boolean into TalkCard (e.g., via an
isUpcoming prop), updating references to isUpcoming and removing useTodayDate
usage.
- Around line 22-25: Add a machine-readable datetime attribute to the <time>
element in TalkCard.vue by setting datetime="{{ talk.date }}" (use the existing
talk.date value, which is already ISO-formatted) so the element becomes
semantically correct and retains metadata; update the <time> tag that currently
displays {{ talk.date }} to include datetime bound to talk.date.

In `@app/components/OgImage/components/OgImageFooter.vue`:
- Around line 10-16: The footer's accent line is absolutely positioned inside
the same wrapper that sizes to the text row (the div with :style containing
position:'absolute', bottom:'60px', left:'60px', right:'60px'), causing the
accent to overlap the footer content; fix by reserving space for the
accent—either add bottom padding to that wrapper (paddingBottom equal to the
accent's height + gap) or move the accent element out of absolute positioning
into normal flow just after the footer content (remove position:'absolute' from
the accent and render it as a block/HR sibling of the text) so the accent
renders below the footer instead of through it.

In `@content.config.ts`:
- Around line 52-54: The talks collection currently uses a glob "talks/**" which
can match nested or non-markdown files; update the defineCollection call for the
talks collection to use the constrained glob "talks/*.md" so it only ingests
top-level markdown talk pages (i.e., change the source value in the talks
defineCollection entry from "talks/**" to "talks/*.md").

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: ASSERTIVE

Plan: Lite

Run ID: f43c7341-507e-448f-b6e0-059082aeaaeb

📥 Commits

Reviewing files that changed from the base of the PR and between 24a8922 and ca5cc98.

📒 Files selected for processing (4)
  • app/components/OgImage/components/OgImageFooter.vue
  • app/components/content/TalkCard.vue
  • app/components/ui/AppCard.vue
  • content.config.ts

Comment thread app/components/content/TalkCard.vue
Comment thread app/components/content/TalkCard.vue Outdated
Comment thread app/components/OgImage/components/OgImageFooter.vue
Comment thread content.config.ts
Copy link
Copy Markdown

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@app/components/content/TalkCard.vue`:
- Around line 45-72: The repeated NuxtLink blocks in TalkCard.vue (the
Slides/Video/Repo links using talk.slidesUrl, talk.videoUrl, talk.repoUrl and
identical classes) should be collapsed into a single render path: build an array
of resource objects (e.g., { key: 'slides', url: talk.slidesUrl, icon:
'ph:presentation', label: 'Slides' } etc.) in the component data/computed and
replace the three NuxtLink elements with one v-for that conditionally renders
only items with a URL, or extract a small ResourceLink child component that
accepts props (url, icon, label) and use it in a v-for; ensure the link
attributes (target="_blank", :to binding, classes, and Icon usage) are preserved
and accessibility text remains the same.
- Around line 2-12: Extract the inline talk shape used in TalkCard.vue into a
shared exported TypeScript interface (e.g., export interface TalkSummary or
TalkCardTalk) in a central types file or a domain module, replace the inline
generic in defineProps<{ talk: ... }> with defineProps<{ talk: TalkSummary }>
and import that type into TalkCard.vue, and update any other components or page
mappers that currently duplicate the same object shape to import and use the new
TalkSummary/TalkCardTalk type so the contract is single-sourced.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yml

Review profile: ASSERTIVE

Plan: Lite

Run ID: 36bd76eb-839d-45f5-b7c2-e981e870f48c

📥 Commits

Reviewing files that changed from the base of the PR and between ca5cc98 and 7a536a9.

📒 Files selected for processing (1)
  • app/components/content/TalkCard.vue

Comment thread app/components/content/TalkCard.vue
Comment thread app/components/content/TalkCard.vue
@toddeTV toddeTV merged commit d895cae into main Mar 7, 2026
5 checks passed
@toddeTV toddeTV deleted the feat/implement-talks-pages-and-data branch March 7, 2026 10:36
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant