From 9e6fc48d75bf52ab75411c483c970c791245967e Mon Sep 17 00:00:00 2001 From: amy-mor <283112394+amy-mor@users.noreply.github.com> Date: Fri, 18 Sep 2026 01:33:12 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=E5=85=A8=E4=BD=93=E3=81=AE?= =?UTF-8?q?=E3=83=87=E3=82=B6=E3=82=A4=E3=83=B3=E3=81=AE=E5=90=91=E4=B8=8A?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- app/globals.css | 83 +++++-- app/layout.tsx | 9 +- app/learn/page.tsx | 2 +- app/my-notebooks/[notebookId]/CardRow.tsx | 4 +- .../[notebookId]/CreateCardForm.tsx | 2 +- app/my-notebooks/[notebookId]/page.tsx | 4 +- .../[notebookId]/study/StudyDeck.tsx | 6 +- app/my-notebooks/page.tsx | 2 +- app/page.tsx | 114 +++++---- components/Footer.tsx | 21 +- components/Header.tsx | 32 ++- components/HeaderAuthStatus.tsx | 2 +- components/Sidebar.tsx | 14 +- components/StarColorSettings.tsx | 4 +- components/StarCountEditor.tsx | 4 +- components/auth/AuthNavLinks.tsx | 4 +- components/auth/LoginForm.tsx | 2 +- components/auth/LogoutButton.tsx | 2 +- components/auth/SignupForm.tsx | 2 +- components/background.tsx | 217 ++++++++++++------ components/book-shape.tsx | 24 ++ components/home/MaterialsPreview.tsx | 105 +++++++++ components/home/StudyModePreview.tsx | 47 ++++ components/learn/SectionCard.tsx | 2 +- components/my-notebooks/CardFieldsForm.tsx | 4 +- components/my-notebooks/ImportForm.tsx | 4 +- components/my-notebooks/MultiElement.tsx | 20 +- .../my-notebooks/ResetAllStarsButton.tsx | 2 +- .../my-notebooks/ShareNotebookButton.tsx | 4 +- components/pinyin/PinyinPractice.tsx | 2 +- components/verbs/ConjugationPractice.tsx | 2 +- ...terials-preview-chinese-pinyin-card-v3.png | Bin 0 -> 83512 bytes .../materials-preview-french-lire-v2.png | Bin 0 -> 73479 bytes .../materials-preview-german-translation.png | Bin 0 -> 106753 bytes .../materials-preview-spanish-aprender-v2.png | Bin 0 -> 71362 bytes 35 files changed, 542 insertions(+), 204 deletions(-) create mode 100644 components/book-shape.tsx create mode 100644 components/home/MaterialsPreview.tsx create mode 100644 components/home/StudyModePreview.tsx create mode 100644 public/images/materials-preview-chinese-pinyin-card-v3.png create mode 100644 public/images/materials-preview-french-lire-v2.png create mode 100644 public/images/materials-preview-german-translation.png create mode 100644 public/images/materials-preview-spanish-aprender-v2.png diff --git a/app/globals.css b/app/globals.css index 3ea409e..4965791 100644 --- a/app/globals.css +++ b/app/globals.css @@ -9,7 +9,7 @@ @theme inline { --color-background: var(--background); --color-foreground: var(--foreground); - --font-sans: var(--font-geist-sans); + --font-sans: var(--font-noto-sans-jp); --font-mono: var(--font-geist-mono); } @@ -49,7 +49,7 @@ body { background: var(--background); color: var(--foreground); - font-family: Arial, Helvetica, sans-serif; + font-family: var(--font-noto-sans-jp), Arial, Helvetica, sans-serif; } /* フランス語教材セクション(app/learn/french)専用の MDX タイポグラフィ */ @@ -226,32 +226,81 @@ body { } } -@keyframes vb-scroll-left { - from { - transform: translateX(0); - } - to { - transform: translateX(-50%); - } +/* 画面幅の中心を頂点とする二次曲線に沿って単語が流れる背景アニメーション。 +下に凸(vb-wave-dip)と上に凸(vb-wave-peak)の2本の弧は交差しない。 +各単語は移動しながら、その位置での弧の接線角度にrotateで合わせている。 +頂点どうしの縦方向の距離は、VOCABOOKの見出し(page.tsxでfontSize: 18.7vw)の +実際の文字の高さ(インクの上端〜下端、フォントサイズの0.72倍)の2倍になるように +している。片側の振れ幅はその半分(0.72 * 18.7 * 2 / 2 = 0.72 * 18.7 ≈ 13.464vw)。 +VOCABOOKと同じvw単位で揃えることで、画面の縦横比によらずこの関係が常に成り立つ +ようにしている。 +(このvw値とbackground.tsxのVERTEX_OFFSET_VWは連動しているため、フォントサイズや +文字の高さの比率を変えたら両方を再計算すること) */ +@keyframes vb-wave-dip { + 0% { transform: translate(-50%, -50%) translate(62vw, 0vw) rotate(-14.12deg); } + 5% { transform: translate(-50%, -50%) translate(55.8vw, 2.56vw) rotate(-12.76deg); } + 10% { transform: translate(-50%, -50%) translate(49.6vw, 4.85vw) rotate(-11.38deg); } + 15% { transform: translate(-50%, -50%) translate(43.4vw, 6.87vw) rotate(-9.99deg); } + 20% { transform: translate(-50%, -50%) translate(37.2vw, 8.62vw) rotate(-8.58deg); } + 25% { transform: translate(-50%, -50%) translate(31vw, 10.10vw) rotate(-7.17deg); } + 30% { transform: translate(-50%, -50%) translate(24.8vw, 11.31vw) rotate(-5.75deg); } + 35% { transform: translate(-50%, -50%) translate(18.6vw, 12.25vw) rotate(-4.32deg); } + 40% { transform: translate(-50%, -50%) translate(12.4vw, 12.93vw) rotate(-2.88deg); } + 45% { transform: translate(-50%, -50%) translate(6.2vw, 13.33vw) rotate(-1.44deg); } + 50% { transform: translate(-50%, -50%) translate(0vw, 13.46vw) rotate(0deg); } + 55% { transform: translate(-50%, -50%) translate(-6.2vw, 13.33vw) rotate(1.44deg); } + 60% { transform: translate(-50%, -50%) translate(-12.4vw, 12.93vw) rotate(2.88deg); } + 65% { transform: translate(-50%, -50%) translate(-18.6vw, 12.25vw) rotate(4.32deg); } + 70% { transform: translate(-50%, -50%) translate(-24.8vw, 11.31vw) rotate(5.75deg); } + 75% { transform: translate(-50%, -50%) translate(-31vw, 10.10vw) rotate(7.17deg); } + 80% { transform: translate(-50%, -50%) translate(-37.2vw, 8.62vw) rotate(8.58deg); } + 85% { transform: translate(-50%, -50%) translate(-43.4vw, 6.87vw) rotate(9.99deg); } + 90% { transform: translate(-50%, -50%) translate(-49.6vw, 4.85vw) rotate(11.38deg); } + 95% { transform: translate(-50%, -50%) translate(-55.8vw, 2.56vw) rotate(12.76deg); } + 100% { transform: translate(-50%, -50%) translate(-62vw, 0vw) rotate(14.12deg); } +} + +@keyframes vb-wave-peak { + 0% { transform: translate(-50%, -50%) translate(62vw, 0vw) rotate(14.12deg); } + 5% { transform: translate(-50%, -50%) translate(55.8vw, -2.56vw) rotate(12.76deg); } + 10% { transform: translate(-50%, -50%) translate(49.6vw, -4.85vw) rotate(11.38deg); } + 15% { transform: translate(-50%, -50%) translate(43.4vw, -6.87vw) rotate(9.99deg); } + 20% { transform: translate(-50%, -50%) translate(37.2vw, -8.62vw) rotate(8.58deg); } + 25% { transform: translate(-50%, -50%) translate(31vw, -10.10vw) rotate(7.17deg); } + 30% { transform: translate(-50%, -50%) translate(24.8vw, -11.31vw) rotate(5.75deg); } + 35% { transform: translate(-50%, -50%) translate(18.6vw, -12.25vw) rotate(4.32deg); } + 40% { transform: translate(-50%, -50%) translate(12.4vw, -12.93vw) rotate(2.88deg); } + 45% { transform: translate(-50%, -50%) translate(6.2vw, -13.33vw) rotate(1.44deg); } + 50% { transform: translate(-50%, -50%) translate(0vw, -13.46vw) rotate(0deg); } + 55% { transform: translate(-50%, -50%) translate(-6.2vw, -13.33vw) rotate(-1.44deg); } + 60% { transform: translate(-50%, -50%) translate(-12.4vw, -12.93vw) rotate(-2.88deg); } + 65% { transform: translate(-50%, -50%) translate(-18.6vw, -12.25vw) rotate(-4.32deg); } + 70% { transform: translate(-50%, -50%) translate(-24.8vw, -11.31vw) rotate(-5.75deg); } + 75% { transform: translate(-50%, -50%) translate(-31vw, -10.10vw) rotate(-7.17deg); } + 80% { transform: translate(-50%, -50%) translate(-37.2vw, -8.62vw) rotate(-8.58deg); } + 85% { transform: translate(-50%, -50%) translate(-43.4vw, -6.87vw) rotate(-9.99deg); } + 90% { transform: translate(-50%, -50%) translate(-49.6vw, -4.85vw) rotate(-11.38deg); } + 95% { transform: translate(-50%, -50%) translate(-55.8vw, -2.56vw) rotate(-12.76deg); } + 100% { transform: translate(-50%, -50%) translate(-62vw, 0vw) rotate(-14.12deg); } } -.vb-flow-row { - animation-name: vb-scroll-left; +.vb-wave-item { + position: absolute; + left: 50%; animation-timing-function: linear; animation-iteration-count: infinite; } -.vb-flow-row--fg { - animation-duration: 30s; +.vb-wave-item--dip { + animation-name: vb-wave-dip; } -.vb-flow-row--bg { - animation-duration: 36s; - animation-direction: reverse; +.vb-wave-item--peak { + animation-name: vb-wave-peak; } @media (prefers-reduced-motion: reduce) { - .vb-flow-row { + .vb-wave-item { animation: none; } } diff --git a/app/layout.tsx b/app/layout.tsx index 5fdff47..d3d7163 100644 --- a/app/layout.tsx +++ b/app/layout.tsx @@ -1,5 +1,5 @@ import type { Metadata } from "next"; -import { Geist, Geist_Mono } from "next/font/google"; +import { Geist_Mono, Noto_Sans_JP } from "next/font/google"; import Header from "@/components/Header"; import Footer from "@/components/Footer"; import Sidebar from "@/components/Sidebar"; @@ -7,9 +7,10 @@ import { getAllLanguageSections } from "@/app/learn/content"; import { LANGUAGES } from "@/app/learn/languages"; import "./globals.css"; -const geistSans = Geist({ - variable: "--font-geist-sans", +const notoSansJP = Noto_Sans_JP({ + variable: "--font-noto-sans-jp", subsets: ["latin"], + weight: "variable", }); const geistMono = Geist_Mono({ @@ -32,7 +33,7 @@ export default async function RootLayout({ const allSections = await getAllLanguageSections(LANGUAGES.map((l) => l.languageSlug)); return ( - +
diff --git a/app/learn/page.tsx b/app/learn/page.tsx index 1ee298a..0659b40 100644 --- a/app/learn/page.tsx +++ b/app/learn/page.tsx @@ -27,7 +27,7 @@ export default async function LearnPage() {
diff --git a/app/my-notebooks/[notebookId]/CardRow.tsx b/app/my-notebooks/[notebookId]/CardRow.tsx index 034fdbc..a816229 100644 --- a/app/my-notebooks/[notebookId]/CardRow.tsx +++ b/app/my-notebooks/[notebookId]/CardRow.tsx @@ -19,7 +19,7 @@ function SaveButton() { @@ -204,7 +204,7 @@ export default function CardRow({ diff --git a/app/my-notebooks/[notebookId]/CreateCardForm.tsx b/app/my-notebooks/[notebookId]/CreateCardForm.tsx index e7b22b8..91078b2 100644 --- a/app/my-notebooks/[notebookId]/CreateCardForm.tsx +++ b/app/my-notebooks/[notebookId]/CreateCardForm.tsx @@ -14,7 +14,7 @@ function AddButton() { diff --git a/app/my-notebooks/[notebookId]/page.tsx b/app/my-notebooks/[notebookId]/page.tsx index d3e9873..483164f 100644 --- a/app/my-notebooks/[notebookId]/page.tsx +++ b/app/my-notebooks/[notebookId]/page.tsx @@ -64,14 +64,14 @@ export default async function NotebookPage(props: PageProps<"/my-notebooks/[note {starredCount > 0 && ( ★を復習する({starredCount}語) )} 暗記学習を始める diff --git a/app/my-notebooks/[notebookId]/study/StudyDeck.tsx b/app/my-notebooks/[notebookId]/study/StudyDeck.tsx index 59afe17..2502bac 100644 --- a/app/my-notebooks/[notebookId]/study/StudyDeck.tsx +++ b/app/my-notebooks/[notebookId]/study/StudyDeck.tsx @@ -153,7 +153,7 @@ export default function StudyDeck({ > {columns.map((col) => ( ))} @@ -285,7 +285,7 @@ export default function StudyDeck({ @@ -293,7 +293,7 @@ export default function StudyDeck({ type="button" onClick={goNext} disabled={index === order.length - 1} - className="rounded-full bg-black px-4 py-2 text-sm font-medium text-white transition-colors hover:bg-zinc-800 disabled:opacity-40 dark:bg-zinc-50 dark:text-black dark:hover:bg-zinc-200" + className="rounded-full bg-coral-500 px-4 py-2 text-sm font-medium text-white transition-all hover:-translate-y-0.5 hover:bg-coral-600 hover:shadow-lg hover:shadow-coral-200 disabled:opacity-40 dark:hover:shadow-none" > 次へ diff --git a/app/my-notebooks/page.tsx b/app/my-notebooks/page.tsx index 3e6eea0..c17d7c5 100644 --- a/app/my-notebooks/page.tsx +++ b/app/my-notebooks/page.tsx @@ -55,7 +55,7 @@ export default async function MyNotebooksPage() { {notebooks.map((notebook) => (
  • {notebook.title}

    diff --git a/app/page.tsx b/app/page.tsx index 8b06d73..e719339 100644 --- a/app/page.tsx +++ b/app/page.tsx @@ -1,6 +1,24 @@ import Link from "next/link"; +import { Bebas_Neue, Shippori_Mincho } from "next/font/google"; import Background from "@/components/background"; +import BookShape from "@/components/book-shape"; +import { MaterialsPreview } from "@/components/home/MaterialsPreview"; +import { StudyModePreview } from "@/components/home/StudyModePreview"; + +// トップページのメインコピー「Vocabook」専用の見出しフォント(映画ポスター風の存在感を演出) +const bebasNeue = Bebas_Neue({ + variable: "--font-bebas-neue", + subsets: ["latin"], + weight: "400", +}); + +// キャッチコピー専用の明朝体(洋画の邦題コピーのような、字間の空いた上品な佇まいを演出) +const shipporiMincho = Shippori_Mincho({ + variable: "--font-shippori-mincho", + subsets: ["latin"], + weight: "500", +}); // ルートページ export default function Home() { @@ -8,16 +26,33 @@ export default function Home() {
    -
    - +
    + 多言語対応 語学暗記アプリ -

    - Vocabook -

    -

    + {/* 本の形はここ(h1のラッパー)にぶら下げることで、VOCABOOKの実際の文字幅(vw指定で + 可変)に常に追従する。外側のdivに置くとmax-w-3xlで幅が頭打ちになり、 + VOCABOOKの方が広い画面ではみ出してズレていた */} +

    + +

    + Vocabook +

    +
    +

    自分だけの多次元単語帳を作って共有できる、
    多言語対応の語学暗記アプリ。 @@ -26,31 +61,10 @@ export default function Home() {

    - -
    - - 仮画像 - -

    FR → JA

    -

    liberté

    -
    - - 意味 - - - 発音 - - - 例文 - -
    -

    自由

    -
    +
    + -
    +

    My単語帳

    意味・発音・例文など複数の軸を持つ、自分だけの単語帳を作成。 @@ -59,7 +73,7 @@ export default function Home() {

    - + 単語帳を作ってみる - - -
    - - 仮画像 - -

    対応言語

    -
    - - 中国語 - - - フランス語 - - - ドイツ語 - - - スペイン語 - -
    -
    + +
    + +
    + -
    +

    学習教材

    @@ -140,7 +134,7 @@ export default function Home() {

    - + 教材を見てみる - + + +
    ); diff --git a/components/Footer.tsx b/components/Footer.tsx index a7b7086..3ed6589 100644 --- a/components/Footer.tsx +++ b/components/Footer.tsx @@ -1,5 +1,6 @@ //花沢 import Link from "next/link"; +import { Bebas_Neue } from "next/font/google"; const NAV_LINKS = [ { href: "https://forms.gle/HnspDy2kNqQxPCWA8", label: "ご意見" }, @@ -7,25 +8,37 @@ const NAV_LINKS = [ { href: "https://x.com/utokyo_code", label: "公式X" }, ]; +// フッターの「Vocabook」をヘッダーロゴと統一する見出しフォント +const bebasNeue = Bebas_Neue({ + variable: "--font-bebas-neue", + subsets: ["latin"], + weight: "400", +}); + export default function Footer() { return (