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
140 changes: 84 additions & 56 deletions src/app/page.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,18 @@ export const BASE_PATH = basePath ? basePath : "";
import GitHubIcon from "@mui/icons-material/GitHub";
import { SITE_NAME } from "../../lib/constants";
import KeyboardDoubleArrowDownSharpIcon from "@mui/icons-material/KeyboardDoubleArrowDownSharp";
import GradientBackground from "@/components/gradient-background";
import ScreenEmojis from "@/components/screen-emojis";

import contributors from "../../Contributors.json";
import {
groupContributorsBySection,
latestContributorsColor,
} from "@/utils/contributors-grouping";

export default function Home() {
const contributorsNumber = contributors.length;
const contributorsGroups = groupContributorsBySection(contributors, 3);

return (
<>
Expand Down Expand Up @@ -63,65 +71,85 @@ export default function Home() {
</p>
</section>

<section className="bg-red-600 p-6 pb-0 pt-8 md:px-10 md:pt-10">
<div className="rounded-md bg-white px-5 pb-16 pt-12 md:p-20 md:pt-8">
<h2 className="text-center text-xl font-bold tracking-tighter text-red-600 md:pb-4 md:pt-12 md:text-3xl">
簡単 8 STEP でコントリビューション!
</h2>
<ul className="mx-auto mt-8 w-fit list-inside rounded-md px-2 text-lg font-bold md:text-xl md:leading-7 lg:mb-8 lg:mt-10 lg:flex lg:gap-16 lg:bg-stone-100 lg:px-12 lg:pb-4 lg:pt-8">
<div>
<li className="pb-4 md:pb-6">
<p className="mr-6 text-sm text-red-600 md:text-lg">STEP 1</p>
プロジェクトをフォーク
</li>
<li className="pb-4 md:pb-6">
<p className="mr-6 text-sm text-red-600 md:text-lg">STEP 2</p>
ローカルマシンへクローン
</li>
<li className="pb-4 md:pb-6">
<p className="mr-6 text-sm text-red-600 md:text-lg">STEP 3</p>
作業用ブランチを作成
</li>
<li className="pb-4 md:pb-6">
<p className="mr-6 text-sm text-red-600 md:text-lg">STEP 4</p>
変更を加える
</li>
<GradientBackground mainColor={latestContributorsColor}>
<ScreenEmojis contributors={contributorsGroups[0]} isTopSection />
<section className="mx-auto h-screen max-w-screen-xl">
セクション1
</section>

<ScreenEmojis contributors={contributorsGroups[1]} />
<section className="mx-auto h-screen max-w-screen-xl">
セクション2
</section>

<ScreenEmojis contributors={contributorsGroups[2]} />
<section className="bg-red-600 p-6 pb-0 pt-8 md:px-10 md:pt-10">
<div className="rounded-md bg-white px-5 pb-16 pt-12 md:p-20 md:pt-8">
<h2 className="text-center text-xl font-bold tracking-tighter text-red-600 md:pb-4 md:pt-12 md:text-3xl">
簡単 8 STEP でコントリビューション!
</h2>
<ul className="mx-auto mt-8 w-fit list-inside rounded-md px-2 text-lg font-bold md:text-xl md:leading-7 lg:mb-8 lg:mt-10 lg:flex lg:gap-16 lg:bg-stone-100 lg:px-12 lg:pb-4 lg:pt-8">
<div>
<li className="pb-4 md:pb-6">
<p className="mr-6 text-sm text-red-600 md:text-lg">STEP 1</p>
プロジェクトをフォーク
</li>
<li className="pb-4 md:pb-6">
<p className="mr-6 text-sm text-red-600 md:text-lg">STEP 2</p>
ローカルマシンへクローン
</li>
<li className="pb-4 md:pb-6">
<p className="mr-6 text-sm text-red-600 md:text-lg">STEP 3</p>
作業用ブランチを作成
</li>
<li className="pb-4 md:pb-6">
<p className="mr-6 text-sm text-red-600 md:text-lg">STEP 4</p>
変更を加える
</li>
</div>
<div>
<li className="pb-4 md:pb-6">
<p className="mr-6 text-sm text-red-600 md:text-lg">STEP 5</p>
変更をコミット
</li>
<li className="pb-4 md:pb-6">
<p className="mr-6 text-sm text-red-600 md:text-lg">STEP 6</p>
変更をプッシュ
</li>
<li className="pb-4 md:pb-6">
<p className="mr-6 text-sm text-red-600 md:text-lg">STEP 7</p>
プルリクエストを作成
</li>
<li className="pb-4 md:pb-6">
<p className="mr-6 text-sm text-red-600 md:text-lg">STEP 8</p>
レビューとフィードバックに対応
</li>
</div>
</ul>
<div className="mb-8 text-center font-bold leading-7 md:text-xl md:leading-8">
<KeyboardDoubleArrowDownSharpIcon className="mb-4 text-6xl text-red-600" />
<p>プルリクエストが承認されると</p>
<p>あなたの変更がメインプロジェクトに反映されます 🎉</p>
</div>
<div>
<li className="pb-4 md:pb-6">
<p className="mr-6 text-sm text-red-600 md:text-lg">STEP 5</p>
変更をコミット
</li>
<li className="pb-4 md:pb-6">
<p className="mr-6 text-sm text-red-600 md:text-lg">STEP 6</p>
変更をプッシュ
</li>
<li className="pb-4 md:pb-6">
<p className="mr-6 text-sm text-red-600 md:text-lg">STEP 7</p>
プルリクエストを作成
</li>
<li className="pb-4 md:pb-6">
<p className="mr-6 text-sm text-red-600 md:text-lg">STEP 8</p>
レビューとフィードバックに対応
</li>
<div className="text-center">
<a
href="https://github.com/first-contributions-ja/first-contributions-ja.github.io"
className="rounded-md bg-red-600 px-3 py-3 text-sm text-white transition hover:opacity-70 md:px-5 md:py-4 md:text-lg"
>
<GitHubIcon className="mr-3 -translate-y-0.5 text-3xl md:text-4xl" />
詳しい手順はこちら
</a>
</div>
</ul>
<div className="mb-8 text-center font-bold leading-7 md:text-xl md:leading-8">
<KeyboardDoubleArrowDownSharpIcon className="mb-4 text-6xl text-red-600" />
<p>プルリクエストが承認されると</p>
<p>あなたの変更がメインプロジェクトに反映されます 🎉</p>
</div>
<div className="text-center">
<a
href="https://github.com/first-contributions-ja/first-contributions-ja.github.io"
className="rounded-md bg-red-600 px-3 py-3 text-sm text-white transition hover:opacity-70 md:px-5 md:py-4 md:text-lg"
>
<GitHubIcon className="mr-3 -translate-y-0.5 text-3xl md:text-4xl" />
詳しい手順はこちら
</a>
</div>
</div>
</section>
</section>
</GradientBackground>
{/* tailwind のclassをCSSに含める */}
<div className="hidden animate-[horizontal_3s_ease-in-out_infinite_alternate_both]">
horizontal
</div>
<div className="hidden animate-[vertical_3s_ease-in-out_infinite_alternate_both]">
vertical
</div>
</>
);
}
61 changes: 61 additions & 0 deletions src/components/animated-emoji.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
import {
emojiToUnicodeHex,
getAnimationStyles,
getEmojiSize,
} from "@/utils/animated-emoji";
import contributors from "../../Contributors.json";
import { latestContributorsColor } from "@/utils/contributors-grouping";
import { hexToRgb } from "@/utils/background-color";

type AnimatedEmojiProps = {
index: number;
contributor: (typeof contributors)[number];
size?: "small" | "medium" | "large";
isTopSection?: boolean;
};

const AnimatedEmoji: React.FC<AnimatedEmojiProps> = ({
index,
contributor,
isTopSection = false,
}) => {
const emojiSize = getEmojiSize(
isTopSection && index === 0 ? "large" : "medium",
);
const speed = Math.floor(Math.random() * 65 + 35) / 10;
const styles = getAnimationStyles(index, speed);
const notoEmoji = emojiToUnicodeHex(contributor.favoriteEmoji);
const emojiColor = hexToRgb(latestContributorsColor, 0.5);

return (
<>
<div
className="group absolute flex h-20 w-20 cursor-pointer items-center justify-center rounded-full bg-transparent [perspective:1000px]"
style={styles as React.CSSProperties}
>
<div className="relative h-full w-full rounded-full duration-1000 [transform-style:preserve-3d] group-hover:[transform:rotateY(180deg)]">
<div className="absolute h-full w-full rounded-full [backface-visibility:hidden]">
<div
className={`flex h-full w-full flex-col items-center justify-center gap-0.5 ${emojiSize}`}
style={{ color: emojiColor }}
dangerouslySetInnerHTML={{ __html: notoEmoji }}
></div>
</div>
<div
style={{ borderColor: contributor.favoriteColor }}
className="absolute h-full w-full rounded-full border-4 shadow-xl [backface-visibility:hidden] [transform:rotateY(180deg)]"
>
<img
src={`${contributor.github}.png`}
loading="lazy"
alt="contributor's icon"
className="h-full w-full rounded-full object-cover object-top"
/>
</div>
</div>
</div>
</>
);
};

export default AnimatedEmoji;
48 changes: 48 additions & 0 deletions src/components/gradient-background.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
import { getGradientStyles } from "@/utils/background-color";
import { ReactNode } from "react";

type GradientBackgroundProps = {
mainColor: string;
children: ReactNode;
};

const GradientBackground: React.FC<GradientBackgroundProps> = ({
mainColor,
children,
}) => {
const gradientStyles = getGradientStyles(mainColor);

return (
<main
className="bg-gradient-to-b from-[var(--background-start-rgb)] via-[var(--background-middle-rgb)] to-[var(--background-end-rgb)]"
style={gradientStyles as React.CSSProperties}
>
<div className=" inset-0 top-0">
<svg
preserveAspectRatio="none"
viewBox="0 0 1200 120"
xmlns="http://www.w3.org/2000/svg"
style={{
fill: "#ffffff",
width: "100%",
height: 137,
transform: "scaleX(-1)",
}}
>
<path
d="M0 0v46.29c47.79 22.2 103.59 32.17 158 28 70.36-5.37 136.33-33.31 206.8-37.5 73.84-4.36 147.54 16.88 218.2 35.26 69.27 18 138.3 24.88 209.4 13.08 36.15-6 69.85-17.84 104.45-29.34C989.49 25 1113-14.29 1200 52.47V0z"
opacity=".25"
/>
<path
d="M0 0v15.81c13 21.11 27.64 41.05 47.69 56.24C99.41 111.27 165 111 224.58 91.58c31.15-10.15 60.09-26.07 89.67-39.8 40.92-19 84.73-46 130.83-49.67 36.26-2.85 70.9 9.42 98.6 31.56 31.77 25.39 62.32 62 103.63 73 40.44 10.79 81.35-6.69 119.13-24.28s75.16-39 116.92-43.05c59.73-5.85 113.28 22.88 168.9 38.84 30.2 8.66 59 6.17 87.09-7.5 22.43-10.89 48-26.93 60.65-49.24V0z"
opacity=".5"
/>
<path d="M0 0v5.63C149.93 59 314.09 71.32 475.83 42.57c43-7.64 84.23-20.12 127.61-26.46 59-8.63 112.48 12.24 165.56 35.4C827.93 77.22 886 95.24 951.2 90c86.53-7 172.46-45.71 248.8-84.81V0z" />
</svg>
</div>
{children}
</main>
);
};

export default GradientBackground;
6 changes: 3 additions & 3 deletions src/components/header.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,9 +5,9 @@ import { BASE_PATH } from "../app/page";

export default function Header() {
return (
<header className="fixed left-0 top-0 w-full py-3 backdrop-blur-sm lg:py-4">
<header className="fixed left-0 top-0 z-10 w-full py-3 backdrop-blur-sm lg:py-4">
<Link href="/">
<h1 className="ml-3 inline text-lg font-bold text-red-600 transition hover:opacity-70 lg:ml-5">
<span className="ml-3 inline text-lg font-bold text-red-600 transition hover:opacity-70 lg:ml-5">
<Image
src={`${BASE_PATH}/logo.svg`}
width={640}
Expand All @@ -16,7 +16,7 @@ export default function Header() {
className="mr-2 inline-block w-[40px] lg:mr-4"
/>
{SITE_NAME}
</h1>
</span>
</Link>
</header>
);
Expand Down
36 changes: 36 additions & 0 deletions src/components/screen-emojis.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import AnimatedEmoji from "./animated-emoji";
import contributors from "../../Contributors.json";
import { Noto_Emoji } from "next/font/google";

const emoji = Noto_Emoji({
subsets: ["emoji"],
weight: "300",
display: "swap",
});

type ScreenEmojisProps = {
contributors: typeof contributors;
isTopSection?: boolean;
};

const ScreenEmojis: React.FC<ScreenEmojisProps> = ({
contributors,
isTopSection,
}) => {
return (
<>
<div className={`${emoji.className} relative`}>
{contributors.map((contributor, index) => (
<AnimatedEmoji
key={index}
index={index}
contributor={contributor}
isTopSection={isTopSection}
/>
))}
</div>
</>
);
};

export default ScreenEmojis;
39 changes: 39 additions & 0 deletions src/utils/animated-emoji.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
const getEmojiSize = (type: "small" | "medium" | "large") => {
switch (type) {
case "small":
return "text-xl";
case "medium":
return "text-7xl";
case "large":
return "text-9xl";
}
};

const getAnimationDirection = (index: number) =>
(index + 1) % 2 === 0 ? "vertical" : "horizontal";
const getAnimationAlternate = (index: number) =>
index < 5 ? "alternate-reverse" : "alternate";

const getAnimationStyles = (index: number, speed: number) => {
const animationDirection = getAnimationDirection(index);
const isHorizontal = animationDirection === "horizontal";
const alternate = getAnimationAlternate(index);
const left = isHorizontal ? 0 : `calc(100vw / 10 * ${index})`;
const top = isHorizontal ? `calc(80vh / 10 * ${index})` : 0;

return {
animation: `${animationDirection} ${speed}s ease-in-out infinite ${alternate} both`,
left,
top,
};
};

const emojiToUnicodeHex = (emoji: string) => {
const codePoint = emoji.codePointAt(0);
if (codePoint === undefined) {
throw new Error("Invalid emoji input");
}
return `&#x${codePoint.toString(16).toUpperCase()};&#xfe0f;`;
};

export { getEmojiSize, getAnimationStyles, emojiToUnicodeHex };
21 changes: 21 additions & 0 deletions src/utils/background-color.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
export const hexToRgb = (color: string, transparency = 1) => {
const rgbValues = Object.fromEntries(
(
(color.match(/^#?[0-9A-Fa-f]{3}([0-9A-Fa-f]{3})?$/) ? color : "000")
.replace(/^#?(.*)$/, (_, hex) =>
hex.length == 3 ? hex.replace(/./g, "$&$&") : hex,
)
.match(/../g) ?? []
).map((c, i) => ["rgb".charAt(i), parseInt("0x" + c)]),
);

return `rgb(${rgbValues.r} ${rgbValues.g} ${rgbValues.b} / ${transparency})`;
};

export const getGradientStyles = (color: string) => {
return {
"--background-start-rgb": hexToRgb(color, 0.05),
"--background-middle-rgb": hexToRgb(color, 0.4),
"--background-end-rgb": hexToRgb(color),
};
};
Loading