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
163 changes: 148 additions & 15 deletions app/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -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);
}

Expand Down Expand Up @@ -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 タイポグラフィ */
Expand Down Expand Up @@ -226,32 +226,165 @@ body {
}
}

@keyframes vb-scroll-left {
from {
transform: translateX(0);
/* 画面幅の中心を頂点とする二次曲線に沿って単語が流れる背景アニメーション。
下に凸(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);
}
to {
transform: translateX(-50%);
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.1vw) 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.1vw) 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.1vw) 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.1vw) 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;
}
}
9 changes: 5 additions & 4 deletions app/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,15 +1,16 @@
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";
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({
Expand All @@ -32,7 +33,7 @@ export default async function RootLayout({
const allSections = await getAllLanguageSections(LANGUAGES.map((l) => l.languageSlug));

return (
<html lang="ja" className={`${geistSans.variable} ${geistMono.variable} h-full antialiased`}>
<html lang="ja" className={`${notoSansJP.variable} ${geistMono.variable} h-full antialiased`}>
<body className="min-h-full flex flex-col">
<Header />
<div className="flex flex-1 flex-col md:flex-row">
Expand Down
2 changes: 1 addition & 1 deletion app/learn/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ export default async function LearnPage() {
<Link
key={language.languageSlug}
href={`/learn/${language.languageSlug}`}
className="group flex items-center justify-between rounded-2xl border border-black/[.08] bg-white p-6 transition-all hover:border-tealblue-400/60 hover:shadow-sm dark:border-white/[.145] dark:bg-zinc-950 dark:hover:border-tealblue-600/60"
className="group flex items-center justify-between rounded-2xl border border-tealblue-200 bg-white p-6 transition-all hover:-translate-y-1 hover:border-tealblue-300 hover:shadow-xl hover:shadow-tealblue-100 dark:border-tealblue-900/40 dark:bg-zinc-950 dark:hover:border-tealblue-700/60 dark:hover:shadow-none"
>
<div className="flex flex-col gap-1 text-left">
<span className="text-lg font-semibold text-zinc-900 dark:text-zinc-50">
Expand Down
4 changes: 2 additions & 2 deletions app/my-notebooks/[notebookId]/CardRow.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ function SaveButton() {
<button
type="submit"
disabled={pending}
className="rounded-full bg-black px-3 py-1 text-xs font-medium text-white transition-colors hover:bg-zinc-800 disabled:opacity-50 dark:bg-zinc-50 dark:text-black dark:hover:bg-zinc-200"
className="rounded-full bg-coral-500 px-3 py-1 text-xs font-medium text-white transition-all hover:-translate-y-0.5 hover:bg-coral-600 hover:shadow-lg hover:shadow-coral-200 disabled:opacity-50 dark:hover:shadow-none"
>
{pending ? "保存中…" : "保存"}
</button>
Expand Down Expand Up @@ -204,7 +204,7 @@ export default function CardRow({
<button
type="button"
onClick={() => setEditing(false)}
className="text-sm text-zinc-500 transition-colors hover:underline dark:text-zinc-500"
className="text-sm text-coral-600 transition-all hover:-translate-y-0.5 hover:text-coral-800 hover:underline dark:text-coral-400 dark:hover:text-coral-200"
>
キャンセル
</button>
Expand Down
2 changes: 1 addition & 1 deletion app/my-notebooks/[notebookId]/CreateCardForm.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ function AddButton() {
<button
type="submit"
disabled={pending}
className="rounded-full bg-black px-4 py-1.5 text-sm font-medium text-white transition-colors hover:bg-zinc-800 disabled:opacity-50 dark:bg-zinc-50 dark:text-black dark:hover:bg-zinc-200"
className="rounded-full bg-coral-500 px-4 py-1.5 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-50 dark:hover:shadow-none"
>
{pending ? "追加中…" : "追加"}
</button>
Expand Down
4 changes: 2 additions & 2 deletions app/my-notebooks/[notebookId]/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -64,14 +64,14 @@ export default async function NotebookPage(props: PageProps<"/my-notebooks/[note
{starredCount > 0 && (
<Link
href={`/my-notebooks/${notebook.id}/review`}
className="rounded-full border border-amber-400 px-5 py-2 text-sm font-medium text-amber-600 transition-colors hover:bg-amber-50 dark:border-amber-400/60 dark:text-amber-400 dark:hover:bg-amber-400/10"
className="rounded-full border border-coral-300 px-5 py-2 text-sm font-medium text-coral-700 transition-all hover:-translate-y-0.5 hover:border-coral-500 hover:bg-coral-50 hover:text-coral-800 hover:shadow-md hover:shadow-coral-100 dark:border-coral-900/50 dark:text-coral-300 dark:hover:border-coral-600 dark:hover:bg-coral-950/20 dark:hover:text-coral-200 dark:hover:shadow-none"
>
★を復習する({starredCount}語)
</Link>
)}
<Link
href={`/my-notebooks/${notebook.id}/study`}
className="rounded-full bg-black px-5 py-2 text-sm font-medium text-white transition-colors hover:bg-zinc-800 dark:bg-zinc-50 dark:text-black dark:hover:bg-zinc-200"
className="rounded-full bg-coral-500 px-5 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 dark:hover:shadow-none"
>
暗記学習を始める
</Link>
Expand Down
6 changes: 3 additions & 3 deletions app/my-notebooks/[notebookId]/study/StudyDeck.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ export default function StudyDeck({
>
{columns.map((col) => (
<option key={col} value={col}>
: {col}
おもて: {col}
</option>
))}
</select>
Expand Down Expand Up @@ -285,15 +285,15 @@ export default function StudyDeck({
<button
type="button"
onClick={shuffle}
className="rounded-full border border-black/[.08] px-4 py-2 text-sm transition-colors hover:border-black/[.15] dark:border-white/[.145] dark:hover:border-white/[.25]"
className="rounded-full border border-coral-300 px-4 py-2 text-sm text-coral-700 transition-colors hover:border-coral-500 hover:text-coral-800 dark:border-coral-900/50 dark:text-coral-300 dark:hover:border-coral-600 dark:hover:text-coral-200"
>
シャッフル
</button>
<button
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"
>
次へ
</button>
Expand Down
2 changes: 1 addition & 1 deletion app/my-notebooks/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -55,7 +55,7 @@ export default async function MyNotebooksPage() {
{notebooks.map((notebook) => (
<li
key={notebook.id}
className="flex items-center justify-between gap-4 rounded-2xl border border-black/[.08] bg-white p-5 transition-colors hover:border-black/[.15] dark:border-white/[.145] dark:bg-zinc-950 dark:hover:border-white/[.25]"
className="flex items-center justify-between gap-4 rounded-2xl border border-coral-200 bg-white p-5 transition-all hover:-translate-y-1 hover:border-coral-300 hover:shadow-xl hover:shadow-coral-100 dark:border-coral-900/40 dark:bg-zinc-950 dark:hover:border-coral-700/60 dark:hover:shadow-none"
>
<Link href={`/my-notebooks/${notebook.id}`} className="flex-1 text-left">
<p className="font-medium text-black dark:text-zinc-50">{notebook.title}</p>
Expand Down
Loading
Loading