Skip to content

Commit a3c4e04

Browse files
authored
feat: stats and leaderboard renewal (#34)
<!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit * **New Features** * Leaderboards: tier-based filtering, top-tier users carousel, and infinite scrolling * Date period picker to select/reset month for 1-year stats * Monthly contribution line charts and radar visualizations on stats pages * **UI/UX Updates** * "Recent List" renamed to "Leaderboards" with updated labels and hints * Improved responsive layouts, header/link behavior, and horizontal scrolling for lists * Loading states and clearer interactive controls * **Documentation** * Added project coding and Tailwind styling guidelines <sub>✏️ Tip: You can customize this high-level summary in your review settings.</sub> <!-- end of auto-generated comment: release notes by coderabbit.ai -->
1 parent 269a0ef commit a3c4e04

38 files changed

+2232
-384
lines changed

CLAUDE.md

Lines changed: 98 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
# Claude Code Guidelines
2+
3+
## Development Commands
4+
5+
```bash
6+
# Development
7+
bun run dev # Start development server
8+
9+
# Build & Lint
10+
bun run build # Production build
11+
bun run lint # Run ESLint
12+
```
13+
14+
## Project Structure
15+
16+
- `/app` - Next.js App Router pages and components
17+
- `/server` - Server-side services and plugins
18+
- `/src` - Shared utilities, hooks, and localization
19+
- `/public` - Static assets
20+
21+
## Styling Guidelines
22+
23+
### Tailwind Color System
24+
25+
This project uses a custom color palette defined in `tailwind.config.js`. **Always use Tailwind classes instead of hardcoded colors**.
26+
27+
#### Text Colors
28+
```tsx
29+
// Main text - use for primary content
30+
className="text-black dark:text-white"
31+
32+
// Placeholder/secondary text
33+
className="text-placeholder-light dark:text-placeholder-dark"
34+
35+
// Link text
36+
className="text-link-light dark:text-link-dark"
37+
```
38+
39+
#### Background Colors
40+
```tsx
41+
// Paper/card backgrounds
42+
className="bg-paper-light dark:bg-paper-dark"
43+
44+
// Modal backgrounds
45+
className="bg-modal-light dark:bg-modal-dark"
46+
47+
// Gray backgrounds (1=lightest, 9=darkest)
48+
className="bg-gray1 dark:bg-gray8"
49+
className="hover:bg-gray2 dark:hover:bg-gray7"
50+
```
51+
52+
#### Border Colors
53+
```tsx
54+
className="border-border-light dark:border-border-dark"
55+
```
56+
57+
#### Brand/Accent Colors
58+
```tsx
59+
// Primary brand color (blue) - use for primary actions and highlights
60+
className="bg-brand" // #4190EB
61+
62+
// Primary buttons
63+
className="bg-btn-primary-light dark:bg-btn-primary-dark"
64+
className="text-btn-primary-text-light dark:text-btn-primary-text-dark"
65+
66+
// Status colors
67+
className="text-success-light dark:text-success-dark"
68+
className="text-danger-light dark:text-danger-dark"
69+
className="text-warning-light dark:text-warning-dark"
70+
```
71+
72+
### Typography
73+
Use predefined font sizes:
74+
- `text-h1` through `text-h4` for headings
75+
- `text-body1` through `text-body4` for body text
76+
77+
### Common Patterns
78+
79+
#### Dark Mode Support
80+
Always provide both light and dark variants:
81+
```tsx
82+
// Correct
83+
className="bg-paper-light dark:bg-paper-dark text-black dark:text-white"
84+
85+
// Incorrect - hardcoded colors
86+
style={{ backgroundColor: '#1a1a1a', color: '#ffffff' }}
87+
```
88+
89+
#### Hover States
90+
```tsx
91+
className="hover:bg-gray1 dark:hover:bg-gray8"
92+
```
93+
94+
## Component Guidelines
95+
96+
- Use `clsx` for conditional class names
97+
- Prefer Tailwind classes over inline styles
98+
- Support both light and dark modes for all UI components

app/[lang]/(common)/Header/index.tsx

Lines changed: 23 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,16 @@ import Button from '../Button';
2121
import SwitchToggle from './SwitchToggle';
2222

2323
const inter = Inter({subsets: ['latin']});
24+
const normalizePath = (path: string): string => path.replace(/\/+$/, '');
25+
const isActivePath = (
26+
pathname: string | null,
27+
lang: string,
28+
path: string,
29+
): boolean => {
30+
const target = normalizePath(`/${lang}${path}`);
31+
const current = normalizePath(pathname ?? '');
32+
return current === target || current.startsWith(`${target}/`);
33+
};
2434

2535
export type NavLink = {
2636
name: string;
@@ -64,7 +74,9 @@ function DesktopNavMenus(
6474
href={`${link.path}`}
6575
className={clsx(
6676
'text-body4 truncate',
67-
pathname?.includes(link.path) ? 'opacity-100' : 'opacity-30',
77+
isActivePath(pathname, lang, link.path)
78+
? 'opacity-100'
79+
: 'opacity-30',
6880
)}
6981
>
7082
<li
@@ -169,7 +181,9 @@ function MobileNavMenus(
169181
'text-body4 truncate flex-1 h-10 px-8',
170182
'flex items-center',
171183
'hover:opacity-100',
172-
pathname?.includes(link.path) ? 'opacity-100' : 'opacity-30',
184+
isActivePath(pathname, lang, link.path)
185+
? 'opacity-100'
186+
: 'opacity-30',
173187
)}
174188
>
175189
<li
@@ -247,18 +261,18 @@ export default function Header(props: Props): ReactElement {
247261
path: `/stats/${login}`,
248262
},
249263
{
250-
name: t.recentList,
251-
path: '/recent-list',
264+
name: t.leaderboards,
265+
path: '/leaderboards',
252266
},
253267
]
254268
: [
255269
{
256270
name: t.stats,
257-
path: `/stats/`,
271+
path: `/stats`,
258272
},
259273
{
260-
name: t.recentList,
261-
path: '/recent-list',
274+
name: t.leaderboards,
275+
path: '/leaderboards',
262276
},
263277
];
264278

@@ -308,13 +322,15 @@ export default function Header(props: Props): ReactElement {
308322
'h-[56px] decoration-0 bg-basic sticky',
309323
'flex flex-row items-center justify-between',
310324
'px-[28px]',
325+
'w-full min-w-0',
311326
)}
312327
>
313328
<div
314329
className={clsx(
315330
'flex-1 h-14',
316331
'flex flex-row items-center',
317332
'justify-between',
333+
'min-w-0',
318334
)}
319335
>
320336
<div

app/[lang]/(home)/SectionFooter.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -82,7 +82,7 @@ export default function SectionFooter({t}: Props): ReactElement {
8282
/>
8383
<p className={clsx('text-white text-[12px] align-center opacity-50')}>
8484
designed by &nbsp;
85-
<a href="https://hyo.dev">hyochan</a>
85+
<a href="https://github.com/hyochan">hyochan</a>
8686
</p>
8787
</div>
8888
</div>

app/[lang]/layout.tsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,9 +30,10 @@ export default async function LangLayout(props: Props): Promise<ReactElement> {
3030
className={clsx(
3131
'text-center flex-1 self-stretch relative',
3232
'flex flex-col-reverse',
33+
'min-w-0 overflow-x-hidden',
3334
)}
3435
>
35-
<div className={clsx('h-[calc(100vh-56px)]', 'flex')}>
36+
<div className={clsx('h-[calc(100vh-56px)]', 'flex w-full min-w-0')}>
3637
{children}
3738
</div>
3839
<Header

0 commit comments

Comments
 (0)