chore: reloop-v2 initial import for harness breezing QA
This commit is contained in:
85
frontend/src/styles/GlobalStyle.ts
Normal file
85
frontend/src/styles/GlobalStyle.ts
Normal file
@@ -0,0 +1,85 @@
|
||||
'use client';
|
||||
|
||||
import { createGlobalStyle } from 'styled-components';
|
||||
import { theme } from './theme';
|
||||
|
||||
const GlobalStyle = createGlobalStyle`
|
||||
*, *::before, *::after {
|
||||
box-sizing: border-box;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
}
|
||||
|
||||
html {
|
||||
font-size: 16px;
|
||||
-webkit-text-size-adjust: 100%;
|
||||
}
|
||||
|
||||
body {
|
||||
font-family: ${theme.font.sans};
|
||||
background: ${theme.color.bgGradient};
|
||||
background-attachment: fixed;
|
||||
color: ${theme.color.textMain};
|
||||
min-height: 100vh;
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
/* Subtle radial accent glows behind everything */
|
||||
body::before {
|
||||
content: '';
|
||||
position: fixed;
|
||||
inset: -50%;
|
||||
background:
|
||||
radial-gradient(circle at 20% 30%, rgba(99, 102, 241, 0.10) 0%, transparent 50%),
|
||||
radial-gradient(circle at 80% 70%, rgba(139, 92, 246, 0.08) 0%, transparent 50%);
|
||||
z-index: -1;
|
||||
pointer-events: none;
|
||||
}
|
||||
|
||||
a {
|
||||
color: inherit;
|
||||
text-decoration: none;
|
||||
}
|
||||
|
||||
button, input, select, textarea {
|
||||
font-family: inherit;
|
||||
font-size: inherit;
|
||||
}
|
||||
|
||||
/* iOS zoom prevention — inputs must be ≥ 16px */
|
||||
input, select, textarea {
|
||||
font-size: 16px;
|
||||
}
|
||||
|
||||
button {
|
||||
cursor: pointer;
|
||||
border: none;
|
||||
background: transparent;
|
||||
color: inherit;
|
||||
}
|
||||
|
||||
ul, ol { list-style: none; }
|
||||
|
||||
::selection {
|
||||
background: ${theme.color.accent};
|
||||
color: white;
|
||||
}
|
||||
|
||||
/* Scrollbar — subtle */
|
||||
*::-webkit-scrollbar {
|
||||
width: 8px;
|
||||
height: 8px;
|
||||
}
|
||||
*::-webkit-scrollbar-track { background: transparent; }
|
||||
*::-webkit-scrollbar-thumb {
|
||||
background: ${theme.color.border};
|
||||
border-radius: 4px;
|
||||
}
|
||||
*::-webkit-scrollbar-thumb:hover {
|
||||
background: ${theme.color.textMute};
|
||||
}
|
||||
`;
|
||||
|
||||
export default GlobalStyle;
|
||||
30
frontend/src/styles/registry.tsx
Normal file
30
frontend/src/styles/registry.tsx
Normal file
@@ -0,0 +1,30 @@
|
||||
'use client';
|
||||
|
||||
import React, { useState } from 'react';
|
||||
import { useServerInsertedHTML } from 'next/navigation';
|
||||
import { ServerStyleSheet, StyleSheetManager } from 'styled-components';
|
||||
|
||||
/**
|
||||
* styled-components SSR registry for Next.js 14 App Router.
|
||||
*/
|
||||
export default function StyledComponentsRegistry({
|
||||
children,
|
||||
}: {
|
||||
children: React.ReactNode;
|
||||
}) {
|
||||
const [sheet] = useState(() => new ServerStyleSheet());
|
||||
|
||||
useServerInsertedHTML(() => {
|
||||
const styles = sheet.getStyleElement();
|
||||
sheet.instance.clearTag();
|
||||
return <>{styles}</>;
|
||||
});
|
||||
|
||||
if (typeof window !== 'undefined') {
|
||||
return <>{children}</>;
|
||||
}
|
||||
|
||||
return (
|
||||
<StyleSheetManager sheet={sheet.instance}>{children}</StyleSheetManager>
|
||||
);
|
||||
}
|
||||
86
frontend/src/styles/theme.ts
Normal file
86
frontend/src/styles/theme.ts
Normal file
@@ -0,0 +1,86 @@
|
||||
/**
|
||||
* ReLoop v2 design tokens.
|
||||
*
|
||||
* Philosophy: 학습앱 = 집중 + 깔끔. 어두운 그라데이션 배경 유지하되
|
||||
* 정보가 흐리지 않게 surface contrast 를 분명히. 모바일 first (≥ 360px).
|
||||
*/
|
||||
|
||||
export const theme = {
|
||||
color: {
|
||||
bg: '#0b1020',
|
||||
bgGradient:
|
||||
'linear-gradient(160deg, #0b1020 0%, #141934 45%, #1a1530 100%)',
|
||||
surface: '#141934',
|
||||
surface2: '#1c2340',
|
||||
surfaceHover: '#242c4a',
|
||||
border: '#2a3256',
|
||||
borderSoft: '#1f2642',
|
||||
|
||||
textMain: '#f1f5f9',
|
||||
textSub: '#94a3b8',
|
||||
textMute: '#64748b',
|
||||
|
||||
accent: '#6366f1', // indigo — 주 액센트
|
||||
accentHover: '#818cf8',
|
||||
accent2: '#8b5cf6', // violet — 보조
|
||||
|
||||
success: '#22c55e',
|
||||
warning: '#f59e0b',
|
||||
danger: '#ef4444',
|
||||
info: '#38bdf8',
|
||||
},
|
||||
|
||||
persona: {
|
||||
senior: { label: '상위권', color: '#34d399', emoji: '🧊' },
|
||||
mid: { label: '중위권', color: '#60a5fa', emoji: '🌊' },
|
||||
junior: { label: '하위권', color: '#fbbf24', emoji: '🔥' },
|
||||
crammer: { label: '벼락치기형', color: '#f472b6', emoji: '⚡' },
|
||||
},
|
||||
|
||||
space: {
|
||||
xs: '4px',
|
||||
sm: '8px',
|
||||
md: '16px',
|
||||
lg: '24px',
|
||||
xl: '32px',
|
||||
xxl: '48px',
|
||||
},
|
||||
|
||||
radius: {
|
||||
sm: '6px',
|
||||
md: '10px',
|
||||
lg: '16px',
|
||||
xl: '20px',
|
||||
pill: '9999px',
|
||||
},
|
||||
|
||||
font: {
|
||||
sans:
|
||||
"'Pretendard', -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, 'Helvetica Neue', sans-serif",
|
||||
mono:
|
||||
"'JetBrains Mono', ui-monospace, 'SF Mono', Menlo, Consolas, monospace",
|
||||
},
|
||||
|
||||
shadow: {
|
||||
sm: '0 2px 8px rgba(0, 0, 0, 0.3)',
|
||||
md: '0 6px 20px rgba(0, 0, 0, 0.35)',
|
||||
lg: '0 16px 40px rgba(0, 0, 0, 0.45)',
|
||||
glow: '0 0 24px rgba(99, 102, 241, 0.25)',
|
||||
},
|
||||
|
||||
breakpoint: {
|
||||
mobile: '480px',
|
||||
tablet: '768px',
|
||||
desktop: '1024px',
|
||||
wide: '1280px',
|
||||
},
|
||||
};
|
||||
|
||||
export type Theme = typeof theme;
|
||||
|
||||
export const PERSONA_ORDER: Array<keyof typeof theme.persona> = [
|
||||
'senior',
|
||||
'mid',
|
||||
'junior',
|
||||
'crammer',
|
||||
];
|
||||
Reference in New Issue
Block a user