Files
ComfyUI_frontend/apps/website/src/components/common/ContentSection.vue
Yourz bbb043c9cc feat(website): Polish and fix UI (#11363)
## Summary

<!-- One sentence describing what changed and why. -->

Polish and fix UI for new website

## Changes

- **What**: <!-- Core functionality added/modified -->
  - [x] update about video
  - [x] update Moment factory story content
  - [x] update homepage visual
  - [x] update customer story visual
  - [x] put images and videos to bucket

## Review Focus

<!-- Critical design decisions or edge cases that need attention -->

<!-- If this PR fixes an issue, uncomment and update the line below -->
<!-- Fixes #ISSUE_NUMBER -->

## Screenshots (if applicable)

<!-- Add screenshots or video recording to help explain your changes -->

┆Issue is synchronized with this [Notion
page](https://www.notion.so/PR-11363-feat-website-Polish-and-fix-UI-3466d73d365081f895aff84b594450c9)
by [Unito](https://www.unito.io)

---------

Co-authored-by: DrJKL <DrJKL0424@gmail.com>
Co-authored-by: Amp <amp@ampcode.com>
Co-authored-by: GitHub Action <action@github.com>
Co-authored-by: Alexander Brown <drjkl@comfy.org>
Co-authored-by: github-actions <github-actions@github.com>
2026-04-22 18:45:27 -07:00

239 lines
7.5 KiB
Vue

<script setup lang="ts">
import { cn } from '@comfyorg/tailwind-utils'
import { useIntersectionObserver, useTemplateRefsList } from '@vueuse/core'
import { computed, ref } from 'vue'
import type { Locale, TranslationKey } from '../../i18n/translations'
import { hasKey, t } from '../../i18n/translations'
import { scrollTo } from '../../scripts/smoothScroll'
import { prefersReducedMotion } from '../../composables/useReducedMotion'
import BrandButton from './BrandButton.vue'
import CategoryNav from './CategoryNav.vue'
import SectionLabel from './SectionLabel.vue'
import { deriveSections } from '../../config/contentSections'
const {
prefix,
locale = 'en',
readMoreHref
} = defineProps<{
prefix: string
locale?: Locale
readMoreHref?: string
}>()
const sections = deriveSections(prefix)
function key(sectionId: string, suffix: string): TranslationKey {
return `${prefix}.${sectionId}.${suffix}` as TranslationKey
}
const categories = computed(() =>
sections.map((s) => ({
label: t(key(s.id, 'label'), locale),
value: s.id
}))
)
const activeSection = ref(sections[0]?.id ?? '')
const sectionRefs = useTemplateRefsList<HTMLElement>()
let isScrolling = false
const HEADER_OFFSET = -144
useIntersectionObserver(
sectionRefs,
(entries) => {
if (isScrolling) return
let best: IntersectionObserverEntry | null = null
for (const entry of entries) {
if (!entry.isIntersecting) continue
if (!best || entry.boundingClientRect.top < best.boundingClientRect.top)
best = entry
}
if (best) activeSection.value = best.target.id
},
{ rootMargin: '-20% 0px -60% 0px' }
)
function scrollToSection(id: string) {
activeSection.value = id
isScrolling = true
const el = document.getElementById(id)
if (el) {
scrollTo(el, {
offset: HEADER_OFFSET,
duration: 0.8,
immediate: prefersReducedMotion(),
onComplete: () => {
isScrolling = false
}
})
return
}
isScrolling = false
}
</script>
<template>
<section class="px-4 pt-8 pb-24 lg:px-20 lg:pt-24 lg:pb-40">
<div class="lg:flex lg:gap-16">
<!-- Desktop sticky nav -->
<aside class="scrollbar-none hidden lg:block lg:w-48 lg:shrink-0">
<div class="sticky top-32">
<CategoryNav
:categories="categories"
:model-value="activeSection"
@update:model-value="scrollToSection"
/>
</div>
</aside>
<!-- Content -->
<div class="flex-1">
<div
v-for="section in sections"
:id="section.id"
:ref="sectionRefs.set"
:key="section.id"
class="mb-16 scroll-mt-24 lg:scroll-mt-36"
>
<h2
v-if="section.hasTitle"
class="text-primary-comfy-canvas mb-6 text-2xl font-light"
>
{{ t(key(section.id, 'title'), locale) }}
</h2>
<template v-for="(block, i) in section.blocks" :key="i">
<!-- Paragraph -->
<p
v-if="block.type === 'paragraph'"
class="text-primary-comfy-canvas mt-4 text-sm/relaxed"
v-html="t(key(section.id, `block.${i}`), locale)"
/>
<!-- Heading (h3) -->
<h3
v-else-if="block.type === 'heading'"
class="text-primary-comfy-yellow mt-6 mb-2 text-lg font-semibold italic"
>
{{ t(key(section.id, `block.${i}.heading`), locale) }}
</h3>
<!-- Bullet list -->
<ul
v-else-if="block.type === 'list'"
class="mt-4 space-y-1 pl-5 text-sm"
>
<li
v-for="(item, j) in t(
key(section.id, `block.${i}`),
locale
).split('\n')"
:key="j"
class="text-primary-comfy-canvas flex items-start gap-2"
>
<span
class="bg-primary-comfy-yellow mt-1.5 size-1.5 shrink-0 rounded-full"
/>
{{ item }}
</li>
</ul>
<!-- Ordered list -->
<ol
v-else-if="block.type === 'ordered-list'"
class="mt-4 space-y-1 pl-1 text-sm"
>
<li
v-for="(item, j) in t(
key(section.id, `block.${i}.ol`),
locale
).split('\n')"
:key="j"
class="text-primary-comfy-canvas flex items-start gap-3"
>
<span
class="text-primary-comfy-yellow shrink-0 font-semibold tabular-nums"
>
{{ String(j + 1).padStart(2, '0') }}
</span>
{{ item }}
</li>
</ol>
<!-- Image with caption -->
<figure v-else-if="block.type === 'image'" class="my-8">
<img
:src="t(key(section.id, `block.${i}.src`), locale)"
:alt="t(key(section.id, `block.${i}.alt`), locale)"
class="w-full rounded-2xl object-cover"
/>
<figcaption class="text-primary-comfy-canvas mt-3 text-xs">
{{ t(key(section.id, `block.${i}.caption`), locale) }}
</figcaption>
</figure>
<!-- Blockquote -->
<blockquote
v-else-if="block.type === 'blockquote'"
:class="
cn(
'border-primary-comfy-yellow my-8 rounded-2xl border-l-4 p-8',
'bg-(--site-bg-soft)'
)
"
>
<p
class="text-primary-comfy-canvas text-lg/relaxed font-light italic"
>
"{{ t(key(section.id, `block.${i}.text`), locale) }}"
</p>
<p class="text-primary-comfy-yellow mt-4 text-sm font-semibold">
{{ t(key(section.id, `block.${i}.name`), locale) }}
</p>
</blockquote>
<!-- Author card -->
<div
v-else-if="block.type === 'author'"
:class="cn('mt-8 rounded-2xl p-6', 'bg-(--site-bg-soft)')"
>
<SectionLabel>
{{ t(key(section.id, `block.${i}.label`), locale) }}
</SectionLabel>
<p class="text-primary-comfy-canvas mt-2 text-sm font-semibold">
{{ t(key(section.id, `block.${i}.name`), locale) }}
</p>
<p class="text-primary-comfy-canvas text-xs">
{{ t(key(section.id, `block.${i}.role`), locale) }}
</p>
<template v-if="hasKey(key(section.id, `block.${i}.name2`))">
<p class="text-primary-comfy-canvas mt-4 text-sm font-semibold">
{{ t(key(section.id, `block.${i}.name2`), locale) }}
</p>
<p class="text-primary-comfy-canvas text-xs">
{{ t(key(section.id, `block.${i}.role2`), locale) }}
</p>
</template>
</div>
</template>
</div>
<!-- Read more CTA -->
<div v-if="readMoreHref" class="mt-8 flex justify-center">
<BrandButton :href="readMoreHref" variant="solid" size="lg">
<span class="ppformula-text-center flex items-center gap-2">
{{ t('customers.story.readMore' as TranslationKey, locale) }}
<span class="text-base"></span>
</span>
</BrandButton>
</div>
</div>
</div>
</section>
</template>