diff --git a/.vscode/settings.json b/.vscode/settings.json index fb4e7cb..985d918 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,5 +1,16 @@ { - "cSpell.words": ["ACMRTUXB", "eslintcache", "isrc", "TTFB"], + "cSpell.words": [ + "ACMRTUXB", + "Cantarell", + "coments", + "eslintcache", + "Fira", + "isrc", + "Neue", + "Pretendard", + "Segoe", + "TTFB" + ], "css.lint.unknownAtRules": "ignore", "typescript.preferences.importModuleSpecifier": "non-relative", "typescript.suggest.autoImports": true, diff --git a/package.json b/package.json index e0496a8..59b43b4 100644 --- a/package.json +++ b/package.json @@ -33,6 +33,7 @@ "dependencies": { "@emotion/react": "^11.14.0", "@emotion/styled": "^11.14.0", + "@mui/icons-material": "^7.1.2", "@mui/material": "^7.1.2", "@tanstack/react-query": "^5.81.2", "firebase": "^11.9.1", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 72d687a..4a92bce 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -14,6 +14,9 @@ importers: '@emotion/styled': specifier: ^11.14.0 version: 11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0) + '@mui/icons-material': + specifier: ^7.1.2 + version: 7.1.2(@mui/material@7.1.2(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@types/react@19.1.8)(react@19.1.0) '@mui/material': specifier: ^7.1.2 version: 7.1.2(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) @@ -776,6 +779,17 @@ packages: '@mui/core-downloads-tracker@7.1.2': resolution: {integrity: sha512-0gLO1PvbJwSYe5ji021tGj6HFqrtEPMGKK4L1zWwRbhzrWWUumUJvMvJUsIgWQIYQsgOnhq9k2Fc1BxLGHDsAg==} + '@mui/icons-material@7.1.2': + resolution: {integrity: sha512-slqJByDub7Y1UcokrM17BoMBMvn8n7daXFXVoTv0MEH5k3sHjmsH8ql/Mt3s9vQ20cORDr83UZ448TEGcbrXtw==} + engines: {node: '>=14.0.0'} + peerDependencies: + '@mui/material': ^7.1.2 + '@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0 + react: ^17.0.0 || ^18.0.0 || ^19.0.0 + peerDependenciesMeta: + '@types/react': + optional: true + '@mui/material@7.1.2': resolution: {integrity: sha512-Z5PYKkA6Kd8vS04zKxJNpwuvt6IoMwqpbidV7RCrRQQKwczIwcNcS8L6GnN4pzFYfEs+N9v6co27DmG07rcnoA==} engines: {node: '>=14.0.0'} @@ -3744,6 +3758,14 @@ snapshots: '@mui/core-downloads-tracker@7.1.2': {} + '@mui/icons-material@7.1.2(@mui/material@7.1.2(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0))(@types/react@19.1.8)(react@19.1.0)': + dependencies: + '@babel/runtime': 7.27.6 + '@mui/material': 7.1.2(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0) + react: 19.1.0 + optionalDependencies: + '@types/react': 19.1.8 + '@mui/material@7.1.2(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react@19.1.0))(@types/react@19.1.8)(react-dom@19.1.0(react@19.1.0))(react@19.1.0)': dependencies: '@babel/runtime': 7.27.6 diff --git a/src/app/entry/main.tsx b/src/app/entry/main.tsx index ba326ef..45f2481 100644 --- a/src/app/entry/main.tsx +++ b/src/app/entry/main.tsx @@ -1,15 +1,20 @@ +import { ThemeProvider } from "@mui/material"; import { QueryClientProvider } from "@tanstack/react-query"; import { StrictMode } from "react"; import { createRoot } from "react-dom/client"; import App from "@app/routes/App"; +import theme from "@app/styles/theme"; +import "@app/styles/global.css"; import queryClient from "@shared/react-query/queryClient"; createRoot(document.getElementById("root")!).render( - + + + ); diff --git a/src/app/styles/App.css b/src/app/styles/App.css deleted file mode 100644 index b9d355d..0000000 --- a/src/app/styles/App.css +++ /dev/null @@ -1,42 +0,0 @@ -#root { - max-width: 1280px; - margin: 0 auto; - padding: 2rem; - text-align: center; -} - -.logo { - height: 6em; - padding: 1.5em; - will-change: filter; - transition: filter 300ms; -} -.logo:hover { - filter: drop-shadow(0 0 2em #646cffaa); -} -.logo.react:hover { - filter: drop-shadow(0 0 2em #61dafbaa); -} - -@keyframes logo-spin { - from { - transform: rotate(0deg); - } - to { - transform: rotate(360deg); - } -} - -@media (prefers-reduced-motion: no-preference) { - a:nth-of-type(2) .logo { - animation: logo-spin infinite 20s linear; - } -} - -.card { - padding: 2em; -} - -.read-the-docs { - color: #888; -} diff --git a/src/app/styles/global.css b/src/app/styles/global.css new file mode 100644 index 0000000..43167dc --- /dev/null +++ b/src/app/styles/global.css @@ -0,0 +1,16 @@ +html { + font-size: 62.5%; +} + +body { + font-size: 1.6rem; + margin: 0; + padding: 0; + box-sizing: border-box; +} + +*, +*::before, +*::after { + box-sizing: inherit; +} diff --git a/src/app/styles/theme.ts b/src/app/styles/theme.ts new file mode 100644 index 0000000..cfbf6cb --- /dev/null +++ b/src/app/styles/theme.ts @@ -0,0 +1,447 @@ +import { createTheme } from "@mui/material/styles"; + +const theme = createTheme({ + breakpoints: { + values: { + xs: 0, + sm: 600, + md: 960, + lg: 1280, + xl: 1920, + }, + }, + + palette: { + mode: "light", + primary: { + main: "#2563eb", + light: "#60a5fa", + dark: "#1e40af", + contrastText: "#ffffff", + }, + secondary: { + main: "#475569", + light: "#94a3b8", + dark: "#1e293b", + contrastText: "#ffffff", + }, + success: { + main: "#16a34a", + light: "#4ade80", + dark: "#166534", + contrastText: "#ffffff", + }, + warning: { + main: "#eab308", + light: "#facc15", + dark: "#a16207", + contrastText: "#0f172a", + }, + error: { + main: "#dc2626", + light: "#f87171", + dark: "#991b1b", + contrastText: "#ffffff", + }, + background: { + default: "#f8fafc", + paper: "#ffffff", + }, + text: { + primary: "#0f172a", + secondary: "#475569", + }, + divider: "#e2e8f0", + }, + + typography: { + fontFamily: + "Pretendard, -apple-system, BlinkMacSystemFont, 'Segoe UI', 'Roboto', 'Oxygen', 'Ubuntu', 'Cantarell', 'Fira Sans', 'Droid Sans', 'Helvetica Neue', sans-serif", + + h1: { + fontWeight: 700, + lineHeight: 1.2, + letterSpacing: "-0.025em", + fontSize: "2.8rem", + "@media (min-width:600px)": { + fontSize: "3.2rem", + }, + "@media (min-width:960px)": { + fontSize: "4rem", + }, + }, + h2: { + fontWeight: 600, + lineHeight: 1.3, + letterSpacing: "-0.025em", + fontSize: "2.4rem", + "@media (min-width:600px)": { + fontSize: "2.8rem", + }, + "@media (min-width:960px)": { + fontSize: "3.2rem", + }, + }, + h3: { + fontWeight: 600, + lineHeight: 1.4, + letterSpacing: "-0.02em", + fontSize: "2rem", + "@media (min-width:600px)": { + fontSize: "2.2rem", + }, + "@media (min-width:960px)": { + fontSize: "2.4rem", + }, + }, + h4: { + fontWeight: 600, + lineHeight: 1.4, + fontSize: "1.8rem", + "@media (min-width:600px)": { + fontSize: "1.9rem", + }, + "@media (min-width:960px)": { + fontSize: "2rem", + }, + }, + h5: { + fontWeight: 600, + lineHeight: 1.5, + fontSize: "1.6rem", + "@media (min-width:600px)": { + fontSize: "1.7rem", + }, + "@media (min-width:960px)": { + fontSize: "1.8rem", + }, + }, + h6: { + fontWeight: 600, + lineHeight: 1.5, + fontSize: "1.4rem", + "@media (min-width:600px)": { + fontSize: "1.5rem", + }, + "@media (min-width:960px)": { + fontSize: "1.6rem", + }, + }, + + body1: { + fontSize: "1.4rem", + lineHeight: 1.6, + "@media (min-width:600px)": { + fontSize: "1.5rem", + lineHeight: 1.65, + }, + "@media (min-width:960px)": { + fontSize: "1.6rem", + lineHeight: 1.7, + }, + }, + body2: { + fontSize: "1.2rem", + lineHeight: 1.5, + "@media (min-width:600px)": { + fontSize: "1.3rem", + lineHeight: 1.55, + }, + "@media (min-width:960px)": { + fontSize: "1.4rem", + lineHeight: 1.6, + }, + }, + button: { + fontSize: "1.2rem", + fontWeight: 500, + textTransform: "none", + "@media (min-width:600px)": { + fontSize: "1.3rem", + }, + "@media (min-width:960px)": { + fontSize: "1.4rem", + }, + }, + }, + + shape: { + borderRadius: 8, + }, + + spacing: (factor: number) => `${factor}rem`, + + components: { + MuiButton: { + styleOverrides: { + root: { + textTransform: "none", + fontWeight: 500, + borderRadius: 8, + boxShadow: "none", + padding: "0.6rem 1.2rem", + fontSize: "1.2rem", + minHeight: "3.6rem", + + "@media (min-width:600px)": { + padding: "0.7rem 1.4rem", + fontSize: "1.3rem", + minHeight: "3.8rem", + }, + + "@media (min-width:960px)": { + padding: "0.8rem 1.6rem", + fontSize: "1.4rem", + minHeight: "4rem", + }, + + "&:hover": { + boxShadow: + "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1)", + }, + }, + sizeSmall: { + padding: "0.4rem 0.8rem", + fontSize: "1.1rem", + minHeight: "2.8rem", + "@media (min-width:600px)": { + padding: "0.5rem 1rem", + fontSize: "1.2rem", + minHeight: "3rem", + }, + "@media (min-width:960px)": { + padding: "0.6rem 1.2rem", + fontSize: "1.3rem", + minHeight: "3.2rem", + }, + }, + sizeLarge: { + padding: "0.8rem 1.6rem", + fontSize: "1.4rem", + minHeight: "4.4rem", + "@media (min-width:600px)": { + padding: "1rem 2rem", + fontSize: "1.5rem", + minHeight: "4.6rem", + }, + "@media (min-width:960px)": { + padding: "1.2rem 2.4rem", + fontSize: "1.6rem", + minHeight: "4.8rem", + }, + }, + }, + }, + + MuiCard: { + styleOverrides: { + root: { + borderRadius: 8, + boxShadow: + "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1)", + "& .MuiCardContent-root": { + padding: "1.2rem", + "@media (min-width:600px)": { + padding: "1.6rem", + }, + "@media (min-width:960px)": { + padding: "2rem", + }, + "&:last-child": { + paddingBottom: "1.2rem", + "@media (min-width:600px)": { + paddingBottom: "1.6rem", + }, + "@media (min-width:960px)": { + paddingBottom: "2rem", + }, + }, + }, + + "@media (min-width:600px)": { + borderRadius: 10, + }, + "@media (min-width:960px)": { + borderRadius: 12, + }, + }, + }, + }, + + MuiTextField: { + styleOverrides: { + root: { + "& .MuiOutlinedInput-root": { + borderRadius: 6, + backgroundColor: "#ffffff", + minHeight: "4rem", + + "@media (min-width:600px)": { + borderRadius: 7, + minHeight: "4.4rem", + }, + "@media (min-width:960px)": { + borderRadius: 8, + minHeight: "4.8rem", + }, + + "& .MuiOutlinedInput-input": { + padding: "0.8rem 1.2rem", + "@media (min-width:600px)": { + padding: "1rem 1.4rem", + }, + "@media (min-width:960px)": { + padding: "1.2rem 1.6rem", + }, + }, + + "&:hover": { + "& .MuiOutlinedInput-notchedOutline": { + borderColor: "#60a5fa", + }, + }, + "&.Mui-focused": { + "& .MuiOutlinedInput-notchedOutline": { + borderColor: "#2563eb", + borderWidth: "2px", + }, + }, + }, + + "& .MuiInputLabel-root": { + fontSize: "1.4rem", + "@media (min-width:600px)": { + fontSize: "1.5rem", + }, + "@media (min-width:960px)": { + fontSize: "1.6rem", + }, + }, + }, + }, + }, + + MuiChip: { + styleOverrides: { + root: { + borderRadius: 4, + fontWeight: 500, + height: "2.4rem", + fontSize: "1.1rem", + + "@media (min-width:600px)": { + borderRadius: 5, + height: "2.6rem", + fontSize: "1.2rem", + }, + "@media (min-width:960px)": { + borderRadius: 6, + height: "2.8rem", + fontSize: "1.3rem", + }, + }, + }, + }, + + MuiPaper: { + styleOverrides: { + root: { + borderRadius: 8, + "@media (min-width:600px)": { + borderRadius: 10, + }, + "@media (min-width:960px)": { + borderRadius: 12, + }, + }, + }, + }, + + MuiContainer: { + styleOverrides: { + root: { + paddingLeft: "1.6rem", + paddingRight: "1.6rem", + "@media (min-width:600px)": { + paddingLeft: "2rem", + paddingRight: "2rem", + }, + "@media (min-width:960px)": { + paddingLeft: "2.4rem", + paddingRight: "2.4rem", + }, + }, + }, + }, + + MuiAppBar: { + styleOverrides: { + root: { + boxShadow: + "0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1)", + borderRadius: 0, + "@media (min-width:600px)": { + borderRadius: 0, + }, + "@media (min-width:960px)": { + borderRadius: 0, + }, + "& .MuiToolbar-root": { + minHeight: "5.6rem", + padding: "0 1.6rem", + borderRadius: 0, + "@media (min-width:600px)": { + minHeight: "6rem", + padding: "0 2rem", + borderRadius: 0, + }, + "@media (min-width:960px)": { + minHeight: "6.4rem", + padding: "0 2.4rem", + borderRadius: 0, + }, + }, + }, + }, + }, + + MuiDialog: { + styleOverrides: { + paper: { + borderRadius: 8, + margin: "1.6rem", + maxWidth: "calc(100% - 3.2rem)", + + "@media (min-width:600px)": { + borderRadius: 12, + margin: "3.2rem", + maxWidth: "calc(100% - 6.4rem)", + }, + + "& .MuiDialogTitle-root": { + padding: "1.6rem", + "@media (min-width:600px)": { + padding: "2rem 2.4rem 1.6rem 2.4rem", + }, + }, + + "& .MuiDialogContent-root": { + padding: "0 1.6rem 1.6rem 1.6rem", + "@media (min-width:600px)": { + padding: "0 2.4rem 2rem 2.4rem", + }, + }, + + "& .MuiDialogActions-root": { + padding: "0.8rem 1.6rem 1.6rem 1.6rem", + "@media (min-width:600px)": { + padding: "1.2rem 2.4rem 2rem 2.4rem", + }, + }, + }, + }, + }, + }, +}); + +export default theme; diff --git a/src/shared/firebase/firebase.ts b/src/shared/firebase/firebase.ts index 80a8c99..43f490f 100644 --- a/src/shared/firebase/firebase.ts +++ b/src/shared/firebase/firebase.ts @@ -1,5 +1,5 @@ -import { initializeApp } from "firebase/app"; -import { getFirestore } from "firebase/firestore/lite"; +import { initializeApp, type FirebaseApp } from "firebase/app"; +import { Firestore, getFirestore } from "firebase/firestore/lite"; const firebaseConfig = { apiKey: import.meta.env.VITE_API_KEY, @@ -11,5 +11,5 @@ const firebaseConfig = { measurementId: import.meta.env.VITE_MEASUREMENT_ID, }; -const app = initializeApp(firebaseConfig); -export const db = getFirestore(app); +const app: FirebaseApp = initializeApp(firebaseConfig); +export const db: Firestore = getFirestore(app); diff --git a/vite.config.ts b/vite.config.ts index d923827..cc87eb3 100644 --- a/vite.config.ts +++ b/vite.config.ts @@ -3,7 +3,6 @@ import { resolve } from "path"; import react from "@vitejs/plugin-react"; import { defineConfig } from "vite"; -// https://vite.dev/config/ export default defineConfig({ plugins: [react()], root: "./src/app",