[fix] solution/frontend: 도는 제목이 안 움직이던 것 + 히어로를 입력 카드 구조로 · 헤더 확대
**애니메이션이 안 보이던 진짜 이유** — `.o2o-rotator-track` 이 <span> 이라 display:inline 이었다. 인라인 요소에는 transform 이 적용되지 않는다. 애니메이션은 걸려 있고 화면만 정지였다. (computed style 로는 animationName 이 보여서 더 헷갈린다.) - 히어로를 아임웹 구조로: 한 줄 입력창이 아니라 **큰 입력 카드**, 발행 사이트 띠가 그 카드 **뒤로 full-bleed** 로 지나간다. 카드 아래 따로 두면 첫 화면이 세로로 길어진다 - 카드 안: 브랜드 라벨 + [업종부터 고르기] · 큰 입력 · 하단 업종 안내 + 원형 제출 버튼 - 제목 sm:text-6xl lg:text-7xl, leading 1.15 - 헤더 h-14→h-16, 로고 h-6→h-8, 메뉴 14px, 로그인도 버튼(맨 텍스트면 눌리는 걸로 안 보인다) - 헤더가 커진 만큼 히어로 높이 계산도 4rem 으로 — 안 바꾸면 스크롤바가 생긴다
This commit is contained in:
parent
ee8ba59036
commit
a4ec508ba5
@ -23,9 +23,9 @@ export function MarketingShell({children}: {children: ReactNode}) {
|
||||
return (
|
||||
<div className="min-h-screen bg-background text-foreground">
|
||||
<header className="sticky top-0 z-20 border-b border-border bg-background/85 backdrop-blur">
|
||||
<div className="mx-auto flex h-14 w-full max-w-6xl items-center gap-7 px-5">
|
||||
<div className="mx-auto flex h-16 w-full max-w-6xl items-center gap-8 px-5">
|
||||
<Link to="/" className="flex items-center">
|
||||
<img src="/brand/web4ai-wordmark.svg" alt="Web4Ai" className="h-6 w-auto" />
|
||||
<img src="/brand/web4ai-wordmark.svg" alt="Web4Ai" className="h-8 w-auto" />
|
||||
</Link>
|
||||
|
||||
<nav className="hidden items-center gap-6 sm:flex">
|
||||
@ -35,7 +35,7 @@ export function MarketingShell({children}: {children: ReactNode}) {
|
||||
to={to}
|
||||
className={({isActive}) =>
|
||||
cn(
|
||||
'text-[13px] transition-colors',
|
||||
'text-sm font-medium transition-colors',
|
||||
isActive ? 'text-foreground' : 'text-muted-foreground hover:text-foreground',
|
||||
)
|
||||
}
|
||||
@ -50,18 +50,22 @@ export function MarketingShell({children}: {children: ReactNode}) {
|
||||
{user ? (
|
||||
<Link
|
||||
to="/sites"
|
||||
className="rounded-md bg-primary px-3.5 py-1.5 text-[13px] font-medium text-primary-foreground transition-colors hover:bg-primary/90"
|
||||
className="rounded-md bg-primary px-4 py-2 text-[13px] font-medium text-primary-foreground transition-colors hover:bg-primary/90"
|
||||
>
|
||||
내 사이트
|
||||
</Link>
|
||||
) : (
|
||||
<>
|
||||
<Link to="/login" className="text-[13px] text-muted-foreground transition-colors hover:text-foreground">
|
||||
{/* 아임웹처럼 둘 다 버튼이다 — 로그인만 맨 텍스트면 눌리는 것으로 안 보인다. */}
|
||||
<Link
|
||||
to="/login"
|
||||
className="rounded-md border border-border px-4 py-2 text-[13px] font-medium transition-colors hover:bg-muted"
|
||||
>
|
||||
로그인
|
||||
</Link>
|
||||
<Link
|
||||
to="/builder?new=1"
|
||||
className="rounded-md bg-primary px-3.5 py-1.5 text-[13px] font-medium text-primary-foreground transition-colors hover:bg-primary/90"
|
||||
className="rounded-md bg-primary px-4 py-2 text-[13px] font-medium text-primary-foreground transition-colors hover:bg-primary/90"
|
||||
>
|
||||
무료로 만들기
|
||||
</Link>
|
||||
@ -75,7 +79,7 @@ export function MarketingShell({children}: {children: ReactNode}) {
|
||||
|
||||
<footer className="mt-24 border-t border-border">
|
||||
<div className="mx-auto flex w-full max-w-6xl flex-col gap-3 px-5 py-10 text-xs text-muted-foreground sm:flex-row sm:items-center sm:justify-between">
|
||||
<img src="/brand/web4ai-wordmark.svg" alt="Web4Ai" className="h-5 w-auto opacity-60" />
|
||||
<img src="/brand/web4ai-wordmark.svg" alt="Web4Ai" className="h-6 w-auto opacity-60" />
|
||||
<div className="flex gap-5">
|
||||
{NAV.map(({to, label}) => (
|
||||
<Link key={to} to={to} className="transition-colors hover:text-foreground">
|
||||
|
||||
@ -97,6 +97,9 @@ body {
|
||||
overflow: hidden;
|
||||
}
|
||||
.o2o-rotator-track {
|
||||
/* ★ display:block 이 없으면 아무 일도 안 일어난다. 이 요소는 <span> 이라 기본이 inline 이고,
|
||||
**인라인 요소에는 transform 이 적용되지 않는다** — 애니메이션은 걸려 있는데 화면은 정지다. */
|
||||
display: block;
|
||||
animation: o2o-rotate-6 16s cubic-bezier(0.65, 0, 0.35, 1) infinite;
|
||||
}
|
||||
.o2o-rotator-track > span {
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import {useState, type FormEvent} from 'react';
|
||||
import {Link, useNavigate} from 'react-router';
|
||||
import {ArrowRight, Building2, Check, Coffee, Search, Stethoscope, UtensilsCrossed} from 'lucide-react';
|
||||
import type {IndustryType} from '@o2o/shared';
|
||||
import {ArrowRight, ArrowUp, Check} from 'lucide-react';
|
||||
import {MarketingShell, Section, SectionHead} from '@/components/layout/MarketingShell';
|
||||
import {ShowcaseGrid, ShowcasePeeks} from '@/features/marketing/ShowcaseGrid';
|
||||
import {Button} from '@/components/ui/button';
|
||||
@ -20,12 +19,6 @@ const HEADLINES = [
|
||||
'우리 가게가 직접 말하는',
|
||||
];
|
||||
|
||||
const INDUSTRIES: {id: IndustryType; label: string; icon: typeof Building2}[] = [
|
||||
{id: 'stay', label: '숙박', icon: Building2},
|
||||
{id: 'cafe', label: '카페', icon: Coffee},
|
||||
{id: 'restaurant', label: '음식점', icon: UtensilsCrossed},
|
||||
{id: 'clinic', label: '피부과 · 성형외과', icon: Stethoscope},
|
||||
];
|
||||
|
||||
/**
|
||||
* 랜딩 — 원페이지. 로그인 전 첫 화면이다.
|
||||
@ -55,9 +48,9 @@ export function LandingPage() {
|
||||
return (
|
||||
<MarketingShell>
|
||||
{/* ── 상단 ─────────────────────────────────────── */}
|
||||
<section className="flex min-h-[calc(100dvh-3.5rem)] items-center border-b border-border">
|
||||
<div className="mx-auto w-full max-w-6xl px-5 py-12 text-center">
|
||||
<h1 className="mx-auto max-w-4xl text-4xl leading-[1.25] font-bold tracking-[-0.04em] sm:text-6xl">
|
||||
<section className="relative flex min-h-[calc(100dvh-4rem)] items-center overflow-hidden border-b border-border">
|
||||
<div className="mx-auto w-full max-w-6xl px-5 text-center">
|
||||
<h1 className="mx-auto text-4xl leading-[1.15] font-extrabold tracking-[-0.045em] sm:text-6xl lg:text-7xl">
|
||||
{/* 앞말만 돈다. 마지막 항목은 첫 항목의 복제다 — 되감을 때 글자가 튀지 않게(index.css). */}
|
||||
<span className="o2o-rotator">
|
||||
<span className="o2o-rotator-track">
|
||||
@ -70,46 +63,57 @@ export function LandingPage() {
|
||||
</span>
|
||||
홈페이지
|
||||
</h1>
|
||||
<p className="mx-auto mt-3 max-w-xl text-sm text-muted-foreground sm:text-base">
|
||||
<p className="mx-auto mt-6 text-base text-muted-foreground sm:text-lg">
|
||||
가게 이름 한 줄로 시작해 보세요
|
||||
</p>
|
||||
|
||||
<form onSubmit={submit} className="mx-auto mt-6 flex max-w-lg gap-2">
|
||||
<div className="relative flex-1">
|
||||
<Search
|
||||
className="pointer-events-none absolute top-1/2 left-4 size-4 -translate-y-1/2 text-muted-foreground"
|
||||
aria-hidden
|
||||
/>
|
||||
{/* ★ 입력 카드와 발행 사이트 띠를 **같은 밴드**에 겹친다(아임웹과 같은 구조).
|
||||
띠를 카드 아래 따로 두면 첫 화면이 세로로 길어지고, 카드가 뜬금없이 혼자 뜬다. */}
|
||||
<div className="relative mt-14">
|
||||
<div className="pointer-events-none absolute top-1/2 left-1/2 w-screen -translate-x-1/2 -translate-y-1/2">
|
||||
<ShowcasePeeks />
|
||||
</div>
|
||||
|
||||
<form
|
||||
onSubmit={submit}
|
||||
className="relative z-10 mx-auto max-w-2xl rounded-2xl border border-border bg-card p-4 text-left shadow-[0_18px_50px_-20px_rgb(20_22_34/0.35)]"
|
||||
>
|
||||
<div className="flex items-center gap-2">
|
||||
<img src="/brand/web4ai-wordmark.svg" alt="Web4Ai" className="h-4 w-auto" />
|
||||
<span className="rounded-full bg-muted px-2 py-0.5 text-[10px] font-medium text-muted-foreground">
|
||||
가게 이름으로 시작
|
||||
</span>
|
||||
{/* 업종부터 고르고 싶은 사람을 위한 갈래. 위저드의 업종 단계로 바로 보낸다. */}
|
||||
<Link
|
||||
to="/builder?new=1&step=industry"
|
||||
className="ml-auto rounded-full border border-border px-3 py-1 text-xs text-muted-foreground transition-colors hover:border-foreground/25 hover:text-foreground"
|
||||
>
|
||||
업종부터 고르기
|
||||
</Link>
|
||||
</div>
|
||||
|
||||
<input
|
||||
value={query}
|
||||
onChange={(e) => setQuery(e.target.value)}
|
||||
placeholder="가게 이름을 입력하세요"
|
||||
aria-label="가게 이름"
|
||||
className="h-13 w-full rounded-xl border border-border bg-card pr-5 pl-11 text-sm shadow-sm transition-colors outline-none placeholder:text-muted-foreground focus:border-ring focus:ring-1 focus:ring-ring"
|
||||
className="mt-4 w-full border-0 bg-transparent px-1 text-base outline-none placeholder:text-muted-foreground sm:text-lg"
|
||||
/>
|
||||
</div>
|
||||
<Button type="submit" variant="primary" size="lg" className="h-13 shrink-0 rounded-xl px-7">
|
||||
확인하기
|
||||
</Button>
|
||||
</form>
|
||||
|
||||
{/* 업종은 고르지 않아도 된다 — 여기 있는 건 "누구를 위한 서비스인가" 를 말하기 위한 것이다. */}
|
||||
<div className="mt-3 flex flex-wrap items-center justify-center gap-2">
|
||||
{INDUSTRIES.map(({id, label, icon: Icon}) => (
|
||||
<Link
|
||||
key={id}
|
||||
to={`/builder?new=1&industry=${id}`}
|
||||
className="inline-flex items-center gap-1.5 rounded-full border border-border px-3.5 py-1.5 text-xs text-muted-foreground transition-colors hover:border-foreground/25 hover:text-foreground"
|
||||
>
|
||||
<Icon className="size-3.5" aria-hidden />
|
||||
{label}
|
||||
</Link>
|
||||
))}
|
||||
<div className="mt-8 flex items-center justify-between gap-3">
|
||||
<p className="truncate text-xs text-muted-foreground">
|
||||
숙박 · 카페 · 음식점 · 피부과 · 성형외과
|
||||
</p>
|
||||
<button
|
||||
type="submit"
|
||||
aria-label="확인하기"
|
||||
className="grid size-9 shrink-0 place-items-center rounded-full bg-primary text-primary-foreground transition-colors hover:bg-primary/90"
|
||||
>
|
||||
<ArrowUp className="size-4" aria-hidden />
|
||||
</button>
|
||||
</div>
|
||||
</form>
|
||||
</div>
|
||||
|
||||
|
||||
{/* 입력칸만 있으면 무엇이 나오는지 알 수 없다 — 결과물을 바로 밑에서 흘린다. */}
|
||||
<ShowcasePeeks />
|
||||
</div>
|
||||
</section>
|
||||
|
||||
|
||||
Loading…
Reference in New Issue
Block a user