fix: update for reviews

This commit is contained in:
Yourz
2026-04-14 09:58:12 +08:00
parent 14140db09f
commit a1342c39fb
37 changed files with 251 additions and 133 deletions

View File

@@ -6,9 +6,7 @@ test.describe('Homepage @smoke', () => {
})
test('has correct title', async ({ page }) => {
await expect(page).toHaveTitle(
'Comfy — Professional Control of Visual AI'
)
await expect(page).toHaveTitle('Comfy — Professional Control of Visual AI')
})
test('HeroSection heading is visible', async ({ page }) => {
@@ -24,9 +22,7 @@ test.describe('Homepage @smoke', () => {
})
test('ProductShowcase section is visible', async ({ page }) => {
await expect(
page.getByText('HOW', { exact: true }).first()
).toBeVisible()
await expect(page.getByText('HOW', { exact: true }).first()).toBeVisible()
await expect(
page.getByText(/Connect models, processing steps, and outputs/)
).toBeVisible()
@@ -63,6 +59,7 @@ test.describe('Homepage @smoke', () => {
})
test('BuildWhatSection is visible', async ({ page }) => {
// "DOESN'T EXIST" is the actual badge text rendered in the Build What section
await expect(page.getByText("DOESN'T EXIST")).toBeVisible()
})
})

View File

@@ -13,18 +13,22 @@ test.describe('Desktop navigation @smoke', () => {
test('has all top-level nav items', async ({ page }) => {
const nav = page.getByRole('navigation', { name: 'Main navigation' })
const desktopLinks = nav.locator('.md\\:flex').first()
const desktopLinks = nav.getByTestId('desktop-nav-links')
for (const label of ['PRODUCTS', 'PRICING', 'COMMUNITY', 'RESOURCES', 'COMPANY']) {
await expect(
desktopLinks.getByText(label).first()
).toBeVisible()
for (const label of [
'PRODUCTS',
'PRICING',
'COMMUNITY',
'RESOURCES',
'COMPANY'
]) {
await expect(desktopLinks.getByText(label).first()).toBeVisible()
}
})
test('CTA buttons are visible', async ({ page }) => {
const nav = page.getByRole('navigation', { name: 'Main navigation' })
const desktopCTA = nav.locator('.md\\:flex').last()
const desktopCTA = nav.getByTestId('desktop-nav-cta')
await expect(
desktopCTA.getByRole('link', { name: 'DOWNLOAD LOCAL' })
).toBeVisible()
@@ -41,15 +45,13 @@ test.describe('Desktop dropdown @interaction', () => {
test('hovering PRODUCTS shows dropdown items', async ({ page }) => {
const nav = page.getByRole('navigation', { name: 'Main navigation' })
const desktopLinks = nav.locator('.md\\:flex').first()
const desktopLinks = nav.getByTestId('desktop-nav-links')
const productsButton = desktopLinks.getByRole('button', {
name: /PRODUCTS/i
})
await productsButton.hover()
const dropdown = productsButton
.locator('..')
.locator('.backdrop-blur-md')
const dropdown = productsButton.locator('..').getByTestId('nav-dropdown')
for (const item of [
'Comfy Local',
'Comfy Cloud',
@@ -62,7 +64,7 @@ test.describe('Desktop dropdown @interaction', () => {
test('moving mouse away closes dropdown', async ({ page }) => {
const nav = page.getByRole('navigation', { name: 'Main navigation' })
const desktopLinks = nav.locator('.md\\:flex').first()
const desktopLinks = nav.getByTestId('desktop-nav-links')
await desktopLinks.getByRole('button', { name: /PRODUCTS/i }).hover()
const comfyLocal = nav.getByRole('link', { name: 'Comfy Local' }).first()
@@ -74,7 +76,7 @@ test.describe('Desktop dropdown @interaction', () => {
test('Escape key closes dropdown', async ({ page }) => {
const nav = page.getByRole('navigation', { name: 'Main navigation' })
const desktopLinks = nav.locator('.md\\:flex').first()
const desktopLinks = nav.getByTestId('desktop-nav-links')
await desktopLinks.getByRole('button', { name: /PRODUCTS/i }).hover()
const comfyLocal = nav.getByRole('link', { name: 'Comfy Local' }).first()
@@ -131,9 +133,7 @@ test.describe('Mobile menu @mobile', () => {
await expect(
menu.getByRole('link', { name: 'DOWNLOAD LOCAL' })
).toBeVisible()
await expect(
menu.getByRole('link', { name: 'LAUNCH CLOUD' })
).toBeVisible()
await expect(menu.getByRole('link', { name: 'LAUNCH CLOUD' })).toBeVisible()
})
})
@@ -155,7 +155,7 @@ test.describe('Footer @smoke', () => {
test('copyright text is visible', async ({ page }) => {
await expect(
page.locator('footer').getByText(2026 Comfy Org/)
page.locator('footer').getByText(\d{4} Comfy Org/)
).toBeVisible()
})
})

View File

@@ -7,13 +7,11 @@ test.describe('Desktop layout @smoke', () => {
test('navigation links visible and hamburger hidden', async ({ page }) => {
const nav = page.getByRole('navigation', { name: 'Main navigation' })
const desktopLinks = nav.locator('.md\\:flex').first()
const desktopLinks = nav.getByTestId('desktop-nav-links')
await expect(desktopLinks.getByText('PRODUCTS').first()).toBeVisible()
await expect(desktopLinks.getByText('PRICING').first()).toBeVisible()
await expect(
page.getByRole('button', { name: 'Toggle menu' })
).toBeHidden()
await expect(page.getByRole('button', { name: 'Toggle menu' })).toBeHidden()
})
test('product cards in grid layout', async ({ page }) => {
@@ -44,7 +42,7 @@ test.describe('Mobile layout @mobile', () => {
})
test('SocialProofBar shows two marquee rows on mobile', async ({ page }) => {
const mobileContainer = page.locator('.flex.flex-col.gap-8.md\\:hidden')
const mobileContainer = page.getByTestId('social-proof-mobile')
await expect(mobileContainer).toBeVisible()
})
})

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@@ -5,7 +5,7 @@ export interface FooterLink {
external?: boolean
}
defineProps<{
const { title, links } = defineProps<{
title: string
links: FooterLink[]
}>()

View File

@@ -1,6 +1,8 @@
<script setup lang="ts">
import { computed, onUnmounted, watch } from 'vue'
import { computed, onUnmounted, ref, watch } from 'vue'
import type { Locale } from '../../i18n/translations'
import { t } from '../../i18n/translations'
import BrandButton from './BrandButton.vue'
import type { NavLink } from './NavDesktopLink.vue'
@@ -13,20 +15,20 @@ interface CtaLink {
const {
open = false,
links = [],
ctaLinks = []
ctaLinks = [],
locale = 'en'
} = defineProps<{
open?: boolean
links?: NavLink[]
ctaLinks?: CtaLink[]
locale?: Locale
}>()
const emit = defineEmits<{
close: []
}>()
const activeSection = defineModel<string | null>('activeSection', {
default: null
})
const activeSection = ref<string | null>(null)
const activeSectionItems = computed(
() => links.find((l) => l.label === activeSection.value)?.items
@@ -97,7 +99,7 @@ onUnmounted(() => {
@click="activeSection = null"
>
<span aria-hidden="true">&lsaquo;</span>
BACK
{{ t('nav.back', locale) }}
</button>
<p class="text-primary-warm-gray mb-4 text-sm">

View File

@@ -34,11 +34,14 @@ const emit = defineEmits<{
class="relative"
@mouseenter="link.items?.length && emit('open', link.label)"
@mouseleave="emit('close')"
@focusin="link.items?.length && emit('open', link.label)"
@focusout="emit('close')"
>
<button
v-if="link.items?.length"
type="button"
class="group text-primary-comfy-canvas hover:text-primary-warm-gray flex cursor-pointer items-center gap-1.5 py-3 text-sm font-bold tracking-wide uppercase transition-colors"
aria-haspopup="true"
:aria-expanded="isOpen"
@click="emit('toggle', link.label)"
>
@@ -63,12 +66,15 @@ const emit = defineEmits<{
<div
v-if="link.items?.length"
v-show="isOpen"
role="menu"
data-testid="nav-dropdown"
class="bg-transparency-white-t4 absolute top-full left-0 w-max rounded-xl p-2 shadow-lg backdrop-blur-md"
>
<a
v-for="item in link.items"
:key="item.href"
:href="item.href"
role="menuitem"
class="text-primary-comfy-canvas hover:bg-transparency-white-t4 flex items-center gap-2 rounded-sm p-2 text-xs font-medium tracking-wide transition-colors hover:text-white"
@click="emit('close')"
>

View File

@@ -40,11 +40,31 @@ const topColumns: { title: string; links: FooterLink[] }[] = [
{
title: t('footer.resources', locale),
links: [
{ label: t('footer.blog', locale), href: externalLinks.blog, external: true },
{ label: t('nav.discord', locale), href: externalLinks.discord, external: true },
{ label: t('nav.github', locale), href: externalLinks.github, external: true },
{ label: t('nav.docs', locale), href: externalLinks.docs, external: true },
{ label: t('nav.youtube', locale), href: externalLinks.youtube, external: true }
{
label: t('footer.blog', locale),
href: externalLinks.blog,
external: true
},
{
label: t('nav.discord', locale),
href: externalLinks.discord,
external: true
},
{
label: t('nav.github', locale),
href: externalLinks.github,
external: true
},
{
label: t('nav.docs', locale),
href: externalLinks.docs,
external: true
},
{
label: t('nav.youtube', locale),
href: externalLinks.youtube,
external: true
}
]
}
]
@@ -67,7 +87,10 @@ const contactSection = {
</script>
<template>
<footer ref="footerRef" class="bg-primary-comfy-ink text-primary-comfy-canvas px-6 py-8 lg:px-20">
<footer
ref="footerRef"
class="bg-primary-comfy-ink text-primary-comfy-canvas px-6 py-8 lg:px-20"
>
<div
class="border-primary-warm-gray flex flex-col gap-12 border-t pt-16 lg:gap-0"
>
@@ -75,9 +98,7 @@ const contactSection = {
<div class="flex flex-col gap-12 lg:flex-row lg:gap-0">
<!-- Left: tagline -->
<div class="flex-1">
<p
class="text-2xl font-medium tracking-wide uppercase lg:text-3xl"
>
<p class="text-2xl font-medium tracking-wide uppercase lg:text-3xl">
{{ t('footer.tagline', locale) }}
</p>
</div>
@@ -139,14 +160,13 @@ const contactSection = {
</div>
<!-- Logo + bottom bar -->
<div class="flex flex-col gap-8 lg:flex-row lg:items-end lg:justify-between">
<canvas
ref="canvasRef"
class="mt-12 size-52 opacity-80 lg:mt-24"
/>
<div
class="flex flex-col gap-8 lg:flex-row lg:items-end lg:justify-between"
>
<canvas ref="canvasRef" class="mt-12 size-52 opacity-80 lg:mt-24" />
<div class="flex justify-center gap-6 lg:justify-end">
<p class="text-sm">{{ t('footer.location', locale) }}</p>
<p class="text-sm">&copy; 2026 Comfy Org</p>
<p class="text-sm">&copy; {{ new Date().getFullYear() }} Comfy Org</p>
</div>
</div>
</div>

View File

@@ -18,7 +18,11 @@ const navLinks: NavLink[] = [
items: [
{ label: t('nav.comfyLocal', locale), href: routes.download },
{ label: t('nav.comfyCloud', locale), href: routes.cloud },
{ label: t('nav.comfyApi', locale), href: routes.api, badge: 'NEW' },
{
label: t('nav.comfyApi', locale),
href: routes.api,
badge: t('nav.badgeNew', locale)
},
{ label: t('nav.comfyEnterprise', locale), href: routes.cloudEnterprise }
]
},
@@ -26,18 +30,42 @@ const navLinks: NavLink[] = [
{
label: t('nav.community', locale),
items: [
{ label: t('nav.comfyHub', locale), href: externalLinks.workflows, badge: 'NEW' },
{
label: t('nav.comfyHub', locale),
href: externalLinks.workflows,
badge: t('nav.badgeNew', locale)
},
{ label: t('nav.gallery', locale), href: routes.gallery }
]
},
{
label: t('nav.resources', locale),
items: [
{ label: t('nav.blogs', locale), href: externalLinks.blog, external: true },
{ label: t('nav.github', locale), href: externalLinks.github, external: true },
{ label: t('nav.discord', locale), href: externalLinks.discord, external: true },
{ label: t('nav.docs', locale), href: externalLinks.docs, external: true },
{ label: t('nav.youtube', locale), href: externalLinks.youtube, external: true }
{
label: t('nav.blogs', locale),
href: externalLinks.blog,
external: true
},
{
label: t('nav.github', locale),
href: externalLinks.github,
external: true
},
{
label: t('nav.discord', locale),
href: externalLinks.discord,
external: true
},
{
label: t('nav.docs', locale),
href: externalLinks.docs,
external: true
},
{
label: t('nav.youtube', locale),
href: externalLinks.youtube,
external: true
}
]
},
{
@@ -51,8 +79,16 @@ const navLinks: NavLink[] = [
]
const ctaButtons = [
{ label: t('nav.downloadLocal', locale), href: routes.download, primary: false },
{ label: t('nav.launchCloud', locale), href: externalLinks.app, primary: true }
{
label: t('nav.downloadLocal', locale),
href: routes.download,
primary: false
},
{
label: t('nav.launchCloud', locale),
href: externalLinks.app,
primary: true
}
]
const currentPath = ref('')
@@ -102,18 +138,20 @@ onUnmounted(() => {
</script>
<template>
<MobileMenu :open="mobileMenuOpen" :links="navLinks" :cta-links="ctaButtons" @close="closeMobileMenu" />
<MobileMenu
:open="mobileMenuOpen"
:links="navLinks"
:cta-links="ctaButtons"
:locale="locale"
@close="closeMobileMenu"
/>
<nav
class="bg-primary-comfy-ink fixed inset-x-0 top-0 z-50 flex items-center justify-between px-6 py-5 md:px-20 md:py-8"
aria-label="Main navigation"
>
<a :href="routes.home" aria-label="Comfy home">
<img
src="/icons/logomark.svg"
alt="Comfy"
class="h-8 md:hidden"
/>
<img src="/icons/logomark.svg" alt="Comfy" class="h-8 md:hidden" />
<span
class="hidden h-10 w-36 bg-contain bg-left bg-no-repeat md:block"
style="background-image: url('/icons/logo.svg')"
@@ -122,7 +160,10 @@ onUnmounted(() => {
</a>
<!-- Desktop nav links -->
<div class="hidden items-center gap-10 md:flex">
<div
data-testid="desktop-nav-links"
class="hidden items-center gap-10 md:flex"
>
<NavDesktopLink
v-for="link in navLinks"
:key="link.label"
@@ -136,7 +177,10 @@ onUnmounted(() => {
</div>
<!-- Desktop CTA buttons -->
<div class="hidden items-center gap-2 md:flex">
<div
data-testid="desktop-nav-cta"
class="hidden items-center gap-2 md:flex"
>
<BrandButton
v-for="cta in ctaButtons"
:key="cta.href"
@@ -150,9 +194,11 @@ onUnmounted(() => {
<!-- Mobile hamburger -->
<button
class="flex size-10 items-center justify-center rounded-xl md:hidden"
:class="mobileMenuOpen
? 'border-primary-comfy-yellow border-2 bg-transparent'
: 'bg-primary-comfy-yellow'"
:class="
mobileMenuOpen
? 'border-primary-comfy-yellow border-2 bg-transparent'
: 'bg-primary-comfy-yellow'
"
:aria-label="t('nav.toggleMenu', locale)"
aria-controls="site-mobile-menu"
:aria-expanded="mobileMenuOpen"

View File

@@ -6,8 +6,11 @@ import NodeBadge from '../common/NodeBadge.vue'
const { locale = 'en' } = defineProps<{ locale?: Locale }>()
const row1 = [{ text: 'BUILD WHAT' }]
const row2 = [{ text: "DOESN'T EXIST" }, { text: 'YET' }]
const row1 = [{ text: t('buildWhat.row1', locale) }]
const row2 = [
{ text: t('buildWhat.row2a', locale) },
{ text: t('buildWhat.row2b', locale) }
]
</script>
<template>

View File

@@ -25,10 +25,10 @@ const routes = getRoutes(locale)
>
{{ t('caseStudy.label', locale) }}
</p>
<h2 class="text-primary-comfy-canvas text-5xl font-light">
{{ t('caseStudy.heading', locale).split('\n')[0] }}<br />{{
t('caseStudy.heading', locale).split('\n')[1]
}}
<h2
class="text-primary-comfy-canvas text-5xl font-light whitespace-pre-line"
>
{{ t('caseStudy.heading', locale) }}
</h2>
<p class="text-primary-warm-gray text-base">
{{ t('caseStudy.subheading', locale) }}

View File

@@ -4,7 +4,7 @@ const { title, description } = defineProps<{
description: string
}>()
defineEmits<{
const emit = defineEmits<{
click: []
}>()
</script>
@@ -12,7 +12,7 @@ defineEmits<{
<template>
<button
class="rounded-5xl bg-primary-comfy-yellow text-primary-comfy-ink w-full cursor-pointer p-8 text-left transition-all"
@click="$emit('click')"
@click="emit('click')"
>
<h3 class="text-2xl/tight font-medium">
{{ title }}

View File

@@ -27,11 +27,9 @@ useFrameScrub(canvasRef, {
class="flex flex-col items-center px-4 pt-36 pb-16 lg:px-20 lg:pt-50 lg:pb-8"
>
<h1
class="text-primary-comfy-canvas text-center text-5xl font-light lg:text-8xl"
class="text-primary-comfy-canvas text-center text-5xl font-light whitespace-pre-line lg:text-8xl"
>
{{ t('hero.title', locale).split('\n')[0] }}
<br />
{{ t('hero.title', locale).split('\n')[1] }}
{{ t('hero.title', locale) }}
</h1>
<div class="mt-12 w-full max-w-3xl overflow-hidden rounded-2xl">

View File

@@ -50,11 +50,9 @@ const cards = [
{{ t('products.label', locale) }}
</p>
<h2
class="text-primary-comfy-canvas mt-4 text-4xl font-light lg:text-5xl"
class="text-primary-comfy-canvas mt-4 text-4xl font-light whitespace-pre-line lg:text-5xl"
>
{{ t('products.heading', locale).split('\n')[0] }}
<br />
{{ t('products.heading', locale).split('\n')[1] }}
{{ t('products.heading', locale) }}
</h2>
<p class="text-primary-warm-gray mt-4 text-sm">
{{ t('products.subheading', locale) }}

View File

@@ -28,9 +28,9 @@ const features = [
]
const badgeSegments = [
{ text: 'HOW' },
{ text: t('showcase.badgeHow', locale) },
{ logoSrc: '/icons/logo.svg', logoAlt: 'Comfy' },
{ text: 'WORKS' }
{ text: t('showcase.badgeWorks', locale) }
]
const activeIndex = ref(0)

View File

@@ -24,7 +24,7 @@ const row2 = logos.slice(6)
<div class="animate-marquee hidden items-center gap-2 md:flex">
<div
v-for="(logo, i) in [...logos, ...logos]"
:key="i"
:key="`${logo}-${i}`"
class="flex h-20 w-50 shrink-0 items-center justify-center"
>
<img :src="`/icons/clients/${logo}.svg`" :alt="logo" />
@@ -32,11 +32,14 @@ const row2 = logos.slice(6)
</div>
<!-- Two rows on mobile -->
<div class="flex flex-col gap-8 md:hidden">
<div
data-testid="social-proof-mobile"
class="flex flex-col gap-8 md:hidden"
>
<div class="animate-marquee flex items-center gap-8">
<div
v-for="(logo, i) in [...row1, ...row1]"
:key="i"
:key="`${logo}-${i}`"
class="flex h-14 w-40 shrink-0 items-center justify-center"
>
<img :src="`/icons/clients/${logo}.svg`" :alt="logo" />
@@ -45,7 +48,7 @@ const row2 = logos.slice(6)
<div class="animate-marquee-reverse flex items-center gap-8">
<div
v-for="(logo, i) in [...row2, ...row2]"
:key="i"
:key="`${logo}-${i}`"
class="flex h-14 w-40 shrink-0 items-center justify-center"
>
<img :src="`/icons/clients/${logo}.svg`" :alt="logo" />

View File

@@ -2,7 +2,7 @@
import { onMounted, onUnmounted, ref } from 'vue'
import type { Locale } from '../../i18n/translations'
import { t } from '../../i18n/translations'
import { gsap, ScrollTrigger } from '../../scripts/smoothScroll'
import { gsap, ScrollTrigger } from '../../scripts/gsapSetup'
import { externalLinks } from '../../config/routes'
import BrandButton from '../common/BrandButton.vue'

View File

@@ -1,6 +1,6 @@
import type { Ref } from 'vue'
import { onMounted, onUnmounted } from 'vue'
import { gsap, ScrollTrigger } from '../scripts/smoothScroll'
import { gsap, ScrollTrigger } from '../scripts/gsapSetup'
interface FrameScrubOptions {
frameCount: number
@@ -13,13 +13,14 @@ export function useFrameScrub(
options: FrameScrubOptions
) {
let ctx: gsap.Context | undefined
const images: HTMLImageElement[] = []
onMounted(() => {
if (!canvasRef.value) return
const canvas: HTMLCanvasElement = canvasRef.value
const context = canvas.getContext('2d')!
const images: HTMLImageElement[] = []
const context = canvas.getContext('2d')
if (!context) return
let loadedCount = 0
const resolvedTrigger = options.scrollTrigger(canvas)
@@ -32,16 +33,19 @@ export function useFrameScrub(
context.drawImage(img, 0, 0)
}
function onFrameReady() {
loadedCount++
if (loadedCount === options.frameCount) {
drawFrame(0)
initScrub()
}
}
for (let i = 0; i < options.frameCount; i++) {
const img = new Image()
img.src = options.frameSrc(i)
img.onload = () => {
loadedCount++
if (loadedCount === options.frameCount) {
drawFrame(0)
initScrub()
}
}
img.onload = onFrameReady
img.onerror = onFrameReady
images.push(img)
}
@@ -62,6 +66,11 @@ export function useFrameScrub(
})
onUnmounted(() => {
images.forEach((img) => {
img.onload = null
img.onerror = null
})
images.length = 0
ctx?.revert()
})
}

View File

@@ -49,6 +49,8 @@ const translations = {
'zh-CN':
'浏览和混搭数千个社区共享的工作流。从经过验证的模板开始,按需自定义。'
},
'showcase.badgeHow': { en: 'HOW', 'zh-CN': '如何' },
'showcase.badgeWorks': { en: 'WORKS', 'zh-CN': '运作' },
// UseCaseSection
'useCase.label': {
@@ -214,9 +216,11 @@ const translations = {
// BuildWhatSection
'buildWhat.subtitle': {
en: "Comfy gives you the building blocks to create workflows nobody's imagined yet — and share them with everyone.",
'zh-CN':
'Comfy 为您提供构建模块,创造出前所未有的工作流——并与所有人分享。'
'zh-CN': 'Comfy 为您提供构建模块,创造出前所未有的工作流——并与所有人分享。'
},
'buildWhat.row1': { en: 'BUILD WHAT', 'zh-CN': '构建' },
'buildWhat.row2a': { en: "DOESN'T EXIST", 'zh-CN': '尚不存在的' },
'buildWhat.row2b': { en: 'YET', 'zh-CN': '事物' },
// SiteNav
'nav.products': { en: 'PRODUCTS', 'zh-CN': '产品' },
@@ -245,6 +249,7 @@ const translations = {
'nav.launchCloud': { en: 'LAUNCH CLOUD', 'zh-CN': '启动云端' },
'nav.toggleMenu': { en: 'Toggle menu', 'zh-CN': '切换菜单' },
'nav.back': { en: 'BACK', 'zh-CN': '返回' },
'nav.badgeNew': { en: 'NEW', 'zh-CN': '新' },
// SiteFooter
'footer.tagline': {

View File

@@ -3,5 +3,14 @@ import BaseLayout from '../layouts/BaseLayout.astro'
---
<BaseLayout title="About Us — Comfy">
<!-- TODO: Add page content -->
<section class="flex min-h-[60vh] items-center justify-center px-6">
<div class="text-center">
<h1 class="text-primary-comfy-canvas text-4xl font-light">
Coming Soon
</h1>
<p class="text-primary-warm-gray mt-4 text-sm">
This page is being redesigned. Check back soon.
</p>
</div>
</section>
</BaseLayout>

View File

@@ -3,5 +3,14 @@ import BaseLayout from '../layouts/BaseLayout.astro'
---
<BaseLayout title="Careers — Comfy">
<!-- TODO: Add page content -->
<section class="flex min-h-[60vh] items-center justify-center px-6">
<div class="text-center">
<h1 class="text-primary-comfy-canvas text-4xl font-light">
Coming Soon
</h1>
<p class="text-primary-warm-gray mt-4 text-sm">
This page is being redesigned. Check back soon.
</p>
</div>
</section>
</BaseLayout>

View File

@@ -3,5 +3,14 @@ import BaseLayout from '../layouts/BaseLayout.astro'
---
<BaseLayout title="Download Comfy — Run AI Locally">
<!-- TODO: Add page content -->
<section class="flex min-h-[60vh] items-center justify-center px-6">
<div class="text-center">
<h1 class="text-primary-comfy-canvas text-4xl font-light">
Coming Soon
</h1>
<p class="text-primary-warm-gray mt-4 text-sm">
This page is being redesigned. Check back soon.
</p>
</div>
</section>
</BaseLayout>

View File

@@ -3,5 +3,14 @@ import BaseLayout from '../layouts/BaseLayout.astro'
---
<BaseLayout title="Gallery — Comfy">
<!-- TODO: Add page content -->
<section class="flex min-h-[60vh] items-center justify-center px-6">
<div class="text-center">
<h1 class="text-primary-comfy-canvas text-4xl font-light">
Coming Soon
</h1>
<p class="text-primary-warm-gray mt-4 text-sm">
This page is being redesigned. Check back soon.
</p>
</div>
</section>
</BaseLayout>

View File

@@ -0,0 +1,6 @@
import gsap from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
gsap.registerPlugin(ScrollTrigger)
export { gsap, ScrollTrigger }

View File

@@ -1,30 +1,21 @@
import gsap from 'gsap'
import { ScrollTrigger } from 'gsap/ScrollTrigger'
import Lenis from 'lenis'
import { gsap, ScrollTrigger } from './gsapSetup'
gsap.registerPlugin(ScrollTrigger)
let initialized = false
export async function initSmoothScroll() {
if (initialized) return
initialized = true
function needsLenis(): boolean {
const ua = navigator.userAgent
const isWindows = /Windows/.test(ua)
const isLinux = /Linux/.test(ua) && !/Android/.test(ua)
return isWindows || isLinux
}
if (!isWindows && !isLinux) return
let lenis: Lenis | undefined
export function initSmoothScroll() {
if (lenis) return
if (!needsLenis()) return
lenis = new Lenis()
const { default: Lenis } = await import('lenis')
const lenis = new Lenis()
lenis.on('scroll', ScrollTrigger.update)
gsap.ticker.add((time) => {
lenis!.raf(time * 1000)
})
gsap.ticker.add((time) => lenis.raf(time * 1000))
gsap.ticker.lagSmoothing(0)
}

View File

@@ -3,7 +3,7 @@
@font-face {
font-family: 'PP Formula';
src: url('/fonts/PPFormula-Light.otf') format('opentype');
src: url('/fonts/PPFormula-Light.woff2') format('woff2');
font-weight: 300;
font-style: normal;
font-display: swap;
@@ -11,7 +11,7 @@
@font-face {
font-family: 'PP Formula';
src: url('/fonts/PPFormula-Regular.otf') format('opentype');
src: url('/fonts/PPFormula-Regular.woff2') format('woff2');
font-weight: 400;
font-style: normal;
font-display: swap;
@@ -19,7 +19,7 @@
@font-face {
font-family: 'PP Formula';
src: url('/fonts/PPFormula-Medium.otf') format('opentype');
src: url('/fonts/PPFormula-Medium.woff2') format('woff2');
font-weight: 500;
font-style: normal;
font-display: swap;
@@ -27,7 +27,7 @@
@font-face {
font-family: 'PP Formula';
src: url('/fonts/PPFormula-Semibold.otf') format('opentype');
src: url('/fonts/PPFormula-Semibold.woff2') format('woff2');
font-weight: 600;
font-style: normal;
font-display: swap;
@@ -35,7 +35,7 @@
@font-face {
font-family: 'PP Formula';
src: url('/fonts/PPFormula-Bold.otf') format('opentype');
src: url('/fonts/PPFormula-Bold.woff2') format('woff2');
font-weight: 700;
font-style: normal;
font-display: swap;
@@ -43,7 +43,7 @@
@font-face {
font-family: 'PP Formula Condensed';
src: url('/fonts/PPFormula-CondensedSemibold.otf') format('opentype');
src: url('/fonts/PPFormula-CondensedSemibold.woff2') format('woff2');
font-weight: 600;
font-style: normal;
font-display: swap;