Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions .coderabbit.yml
Original file line number Diff line number Diff line change
Expand Up @@ -110,7 +110,7 @@ reviews:
- `app/components/ui/` - Reusable UI primitives (App* prefix)
- `app/components/layout/` - Site-wide layout (header, footer)
- `app/components/content/` - Domain content components (cards, carousel)
- `app/components/OgImage/` - Satori-rendered OG image templates
- `app/components/OgImageTemplate/` - active Takumi OG image templates
- `content/` - File-based CMS (Markdown, YAML): talks/, projects/, clients/, publications/, socials/
- `public/` - Static assets
- `scripts/` - Maintenance scripts
Expand All @@ -130,7 +130,7 @@ reviews:
- Block order: `<script setup lang="ts">` -> `<template>` -> `<i18n lang="yaml">` -> `<style scoped>`.
- Use `<NuxtLink>` for ALL links (internal and external), never raw `<a>` tags.
- Use `<NuxtImg>` for ALL images (from `@nuxt/image`), never raw `<img>` tags.
Exception: `app/components/OgImage/` must use `<img>` (Satori limitation).
Exception: Takumi OG templates may use raw `<img>` only if `13-og-image.md` allows it for renderer compatibility.
- Never nest `<a>` inside `<a>` (or `<NuxtLink>` inside a linked `AppCard`). Use the stretched-link pattern instead.
- Icons: `<Icon name="collection:name" :size="N" />`.

Expand All @@ -156,14 +156,12 @@ reviews:
### Nuxt Config
- Module-driven architecture. Check module docs before modifying module options.

- path: "app/components/OgImage/**/*.vue"
- path: "app/components/OgImageTemplate/**/*.vue"
instructions: |
OG image components are rendered by Satori (not a browser). Special rules:
- Use inline styles only (no Tailwind, no `<style>` blocks). Hardcode hex colors.
- Use `<img>` for photos, inline `<svg>` for icons (never `<Icon>`, `<NuxtImg>`, or data URI `<img>`).
- Set icon `width`/`height` both as HTML attributes AND in `:style`.
- CRITICAL: nuxt-og-image's flex plugin overrides `flexDirection` to `column` on any `<div>` whose children include block elements (div, p, ul, ol, li, blockquote, pre, hr, table, dl). To preserve `flexDirection: 'row'`, add `class="flex-row"` (any class containing "flex-") to the container. Use `<span>` instead of `<div>` for text inside row containers.
- The flex plugin also forces `flexWrap: 'wrap'` and `gap: '0.2em'` on elements without a class containing "flex-".
OG image components are rendered by Takumi. Follow `.claude/rules/13-og-image.md`.
- Prefer inline styles for renderer-critical layout and effects.
- Use only Takumi-safe patterns already documented in `13-og-image.md`.
- Do not use known failing patterns such as `<style scoped>` for renderer-critical styling, CSS variables, `display: inline`, `flex-basis: auto`, or width keywords like `min-content`, `max-content`, and `fit-content`.

- path: "app/pages/**"
instructions: |
Expand Down
13 changes: 6 additions & 7 deletions app/components/OgImageComponents/OgImageAvatar.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
/**
* Shared avatar for non-Home OG image components.
* Renders a circular avatar with accent gradient border in the top-right corner.
* Satori constraints: inline styles only, no Tailwind, hardcoded colors.
*/
</script>

Expand All @@ -14,21 +13,21 @@
right: '80px',
width: '130px',
height: '130px',
borderRadius: '50%',
borderRadius: '999px',
background: 'linear-gradient(135deg, #00dc82, #27272a)',
padding: '4px',
flexWrap: 'nowrap',
}"
>
<img
alt="Thorsten Seyschab"
<NuxtImg
height="120"
src="/avatar-thorsten-seyschab.jpg"
:style="{
width: '120px',
height: '120px',
borderRadius: '50%',
borderRadius: '999px',
objectFit: 'cover',
}"
>
width="120"
/>
</div>
</template>
1 change: 0 additions & 1 deletion app/components/OgImageComponents/OgImageCategoryLabel.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script setup lang="ts">
/**
* Green uppercase category label for OG images (e.g. "Project", "Talk", "Client Project").
* Satori constraints: inline styles only, no Tailwind, hardcoded colors.
*/
defineProps<{
/** The category text to display (e.g. "Project", "Talk"). */
Expand Down
1 change: 0 additions & 1 deletion app/components/OgImageComponents/OgImageDescription.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script setup lang="ts">
/**
* Description/subtitle text for OG images.
* Satori constraints: inline styles only, no Tailwind, hardcoded colors.
*/
withDefaults(defineProps<{
/** Description text to display. */
Expand Down
5 changes: 1 addition & 4 deletions app/components/OgImageComponents/OgImageFooter.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
/**
* Shared footer for all OG image components.
* Renders author name (left), site URL (right), and a bottom gradient accent line.
* Satori constraints: inline styles only, no Tailwind, hardcoded colors.
*/
</script>

Expand All @@ -17,11 +16,9 @@
>
<!-- Author name and site URL -->
<div
class="flex-row"
:style="{
display: 'flex',
flexDirection: 'row',
flexWrap: 'nowrap',
justifyContent: 'space-between',
alignItems: 'flex-end',
}"
Expand Down Expand Up @@ -52,7 +49,7 @@
position: 'absolute',
bottom: '0',
left: '0',
right: '0',
right: '120px',
height: '4px',
background: 'linear-gradient(90deg, #00dc82, #00c474, transparent)',
}"
Expand Down
1 change: 0 additions & 1 deletion app/components/OgImageComponents/OgImageGlow.vue
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
/**
* Shared ambient glow effects for all OG image components.
* Renders two radial gradient spotlights: top-right and bottom-left.
* Satori constraints: inline styles only, no Tailwind, hardcoded colors.
*/
</script>

Expand Down
3 changes: 1 addition & 2 deletions app/components/OgImageComponents/OgImageTitle.vue
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script setup lang="ts">
/**
* Main title text for OG images.
* Satori constraints: inline styles only, no Tailwind, hardcoded colors.
*/
withDefaults(defineProps<{
/** Title text to display. */
Expand All @@ -21,7 +20,7 @@ withDefaults(defineProps<{
<div
:style="{
fontSize: size === 'lg' ? '72px' : '64px',
fontWeight: 800,
fontWeight: 700,
color: '#fafafa',
lineHeight: 1.05,
letterSpacing: '-0.03em',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@
/**
* Default/fallback OG image component for generic pages.
* Used by [...slug].vue when no specific OG component is configured.
* Rendered by Satori (not a browser) - must use inline styles, <img>, and hardcoded colors.
*/
withDefaults(defineProps<{
/** Page title override. */
Expand All @@ -25,21 +24,22 @@ withDefaults(defineProps<{
justifyContent: 'center',
backgroundColor: '#0a0a0b',
fontFamily: 'Inter, system-ui, sans-serif',
padding: '60px 80px',
position: 'relative',
overflow: 'hidden',
}"
>
<OgImageGlow />
<OgImageAvatar />

<!-- Main content -->
<div
:style="{
display: 'flex',
flexDirection: 'column',
flexWrap: 'nowrap',
justifyContent: 'center',
height: '100%',
maxWidth: '960px',
padding: '60px 80px',
boxSizing: 'border-box',
}"
>
<OgImageTitle size="md" :text="title" />
Expand Down
35 changes: 11 additions & 24 deletions ...components/OgImage/OgImageHome.satori.vue → ...ts/OgImageTemplate/OgImageHome.takumi.vue
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script setup lang="ts">
/**
* Default OG image component for the homepage.
* Rendered by Satori (not a browser) - must use inline styles, <img>, and hardcoded colors.
*/
withDefaults(defineProps<{
/** Page title override. */
Expand All @@ -24,62 +23,50 @@ withDefaults(defineProps<{
justifyContent: 'center',
backgroundColor: '#0a0a0b',
fontFamily: 'Inter, system-ui, sans-serif',
padding: '60px',
position: 'relative',
overflow: 'hidden',
}"
>
<OgImageGlow />

<!-- Subtle horizontal rule -->
<div
:style="{
position: 'absolute',
top: '315px',
left: '60px',
width: '600px',
height: '1px',
background: 'linear-gradient(90deg, rgba(0,220,130,0.15), transparent)',
}"
/>

<!-- Title and description (vertically centered) -->
<div
:style="{
display: 'flex',
flexDirection: 'column',
flexWrap: 'nowrap',
width: '670px',
justifyContent: 'center',
height: '100%',
width: '740px',
padding: '60px 30px 60px 60px',
boxSizing: 'border-box',
}"
>
<OgImageTitle :text="title" />
<OgImageDescription size="lg" :text="description" />
</div>

<!-- Avatar with accent gradient border (large, centered-right) -->
<div
:style="{
position: 'absolute',
top: '115px',
right: '80px',
width: '350px',
height: '350px',
borderRadius: '50%',
borderRadius: '999px',
background: 'linear-gradient(135deg, #00dc82, #00c474)',
padding: '5px',
flexWrap: 'nowrap',
}"
>
<img
alt="Thorsten Seyschab"
<NuxtImg
height="340"
src="/avatar-thorsten-seyschab.jpg"
:style="{
width: '340px',
height: '340px',
borderRadius: '50%',
borderRadius: '999px',
objectFit: 'cover',
}"
>
width="340"
/>
</div>

<OgImageFooter />
Expand Down
20 changes: 5 additions & 15 deletions ...ponents/OgImage/OgImageProject.satori.vue → ...OgImageTemplate/OgImageProject.takumi.vue
100755 → 100644
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
<script setup lang="ts">
/**
* OG image component for project pages.
* Rendered by Satori (not a browser) - must use inline styles, <img>, and hardcoded colors.
*/
defineProps<{
title?: string
Expand All @@ -21,40 +20,38 @@ defineProps<{
justifyContent: 'center',
backgroundColor: '#0a0a0b',
fontFamily: 'Inter, system-ui, sans-serif',
padding: '60px 80px',
position: 'relative',
overflow: 'hidden',
}"
>
<OgImageGlow />
<OgImageAvatar />

<!-- Main content -->
<div
:style="{
display: 'flex',
flexDirection: 'column',
flexWrap: 'nowrap',
justifyContent: 'center',
height: '100%',
maxWidth: '960px',
padding: '60px 80px',
boxSizing: 'border-box',
}"
>
<OgImageCategoryLabel text="Project" />

<!-- Project name with stars -->
<div
class="flex-row"
:style="{
display: 'flex',
flexDirection: 'row',
flexWrap: 'nowrap',
alignItems: 'center',
marginBottom: '20px',
}"
>
<span
:style="{
fontSize: '72px',
fontWeight: 800,
fontWeight: 700,
color: '#fafafa',
lineHeight: 1.05,
letterSpacing: '-0.03em',
Expand All @@ -64,16 +61,13 @@ defineProps<{
</span>
<span
v-if="repoStars"
class="flex-row"
:style="{
display: 'flex',
flexDirection: 'row',
flexWrap: 'nowrap',
alignItems: 'center',
marginLeft: '20px',
}"
>
<!-- Phosphor star icon (regular weight) -->
<svg
height="28"
:style="{ width: '28px', height: '28px', marginRight: '6px' }"
Expand All @@ -100,19 +94,15 @@ defineProps<{

<OgImageDescription v-if="description" :max-length="120" :text="description" />

<!-- Tags -->
<div
v-if="tags?.length"
class="flex-row"
:style="{
display: 'flex',
flexDirection: 'row',
flexWrap: 'nowrap',
alignItems: 'center',
marginTop: '20px',
}"
>
<!-- Phosphor tag icon (regular weight) -->
<svg
height="18"
:style="{ width: '18px', height: '18px', marginRight: '8px' }"
Expand Down
Loading
Loading