diff --git a/package-lock.json b/package-lock.json
index 526bcaf..1f2a7ec 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -22,10 +22,12 @@
"react-scripts": "5.0.1",
"styled-components": "^6.1.11",
"three": "^0.165.0",
+ "typescript": "^4.9.5",
"web-vitals": "^2.1.4"
},
"devDependencies": {
"@types/styled-components": "^5.1.34",
+ "@types/three": "^0.165.0",
"typescript": "^4.9.5"
}
},
@@ -3989,6 +3991,13 @@
"node": ">=10.13.0"
}
},
+ "node_modules/@tweenjs/tween.js": {
+ "version": "23.1.2",
+ "resolved": "https://registry.npmjs.org/@tweenjs/tween.js/-/tween.js-23.1.2.tgz",
+ "integrity": "sha512-kMCNaZCJugWI86xiEHaY338CU5JpD0B97p1j1IKNn/Zto8PgACjQx0UxbHjmOcLl/dDOBnItwD07KmCs75pxtQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/aria-query": {
"version": "5.0.4",
"resolved": "https://registry.npmjs.org/@types/aria-query/-/aria-query-5.0.4.tgz",
@@ -4563,6 +4572,13 @@
"resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz",
"integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw=="
},
+ "node_modules/@types/stats.js": {
+ "version": "0.17.3",
+ "resolved": "https://registry.npmjs.org/@types/stats.js/-/stats.js-0.17.3.tgz",
+ "integrity": "sha512-pXNfAD3KHOdif9EQXZ9deK82HVNaXP5ZIF5RP2QG6OQFNTaY2YIetfrE9t528vEreGQvEPRDDc8muaoYeK0SxQ==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/styled-components": {
"version": "5.1.34",
"resolved": "https://registry.npmjs.org/@types/styled-components/-/styled-components-5.1.34.tgz",
@@ -4588,11 +4604,32 @@
"@types/jest": "*"
}
},
+ "node_modules/@types/three": {
+ "version": "0.165.0",
+ "resolved": "https://registry.npmjs.org/@types/three/-/three-0.165.0.tgz",
+ "integrity": "sha512-AJK8JZAFNBF0kBXiAIl5pggYlzAGGA8geVYQXAcPCEDRbyA+oEjkpUBcJJrtNz6IiALwzGexFJGZG2yV3WsYBw==",
+ "dev": true,
+ "license": "MIT",
+ "dependencies": {
+ "@tweenjs/tween.js": "~23.1.1",
+ "@types/stats.js": "*",
+ "@types/webxr": "*",
+ "fflate": "~0.8.2",
+ "meshoptimizer": "~0.18.1"
+ }
+ },
"node_modules/@types/trusted-types": {
"version": "2.0.7",
"resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz",
"integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw=="
},
+ "node_modules/@types/webxr": {
+ "version": "0.5.18",
+ "resolved": "https://registry.npmjs.org/@types/webxr/-/webxr-0.5.18.tgz",
+ "integrity": "sha512-EM8P5wtYMPUlFbDgKfNoPc5PyhgZvwXz0Wf9gYBfJOpTI8AtPzoLJlvw/D9TmIW/LPLTmwMxWI0axBs3hjLSLg==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/@types/ws": {
"version": "8.5.10",
"resolved": "https://registry.npmjs.org/@types/ws/-/ws-8.5.10.tgz",
@@ -8455,6 +8492,13 @@
"bser": "2.1.1"
}
},
+ "node_modules/fflate": {
+ "version": "0.8.2",
+ "resolved": "https://registry.npmjs.org/fflate/-/fflate-0.8.2.tgz",
+ "integrity": "sha512-cPJU47OaAoCbg0pBvzsgpTPhmhqI5eJjh/JIu8tPj5q+T7iLvW/JAYUqmE7KOB4R1ZyEhzBaIQpQpardBF5z8A==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/file-entry-cache": {
"version": "6.0.1",
"resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz",
@@ -12672,6 +12716,13 @@
"node": ">= 8"
}
},
+ "node_modules/meshoptimizer": {
+ "version": "0.18.1",
+ "resolved": "https://registry.npmjs.org/meshoptimizer/-/meshoptimizer-0.18.1.tgz",
+ "integrity": "sha512-ZhoIoL7TNV4s5B6+rx5mC//fw8/POGyNxS/DZyCJeiZ12ScLfVwRE/GfsxwiTkMYYD5DmK2/JXnEVXqL4rF+Sw==",
+ "dev": true,
+ "license": "MIT"
+ },
"node_modules/methods": {
"version": "1.1.2",
"resolved": "https://registry.npmjs.org/methods/-/methods-1.1.2.tgz",
diff --git a/package.json b/package.json
index db86eb5..ce89b51 100644
--- a/package.json
+++ b/package.json
@@ -17,6 +17,7 @@
"react-scripts": "5.0.1",
"styled-components": "^6.1.11",
"three": "^0.165.0",
+ "typescript": "^4.9.5",
"web-vitals": "^2.1.4"
},
"scripts": {
@@ -45,6 +46,7 @@
},
"devDependencies": {
"@types/styled-components": "^5.1.34",
+ "@types/three": "^0.165.0",
"typescript": "^4.9.5"
}
}
diff --git a/src/App.tsx b/src/App.tsx
index e113e98..42b3167 100644
--- a/src/App.tsx
+++ b/src/App.tsx
@@ -2,13 +2,22 @@ import React from "react";
import { BrowserRouter as Router, Route, Routes } from "react-router-dom";
import MainPage from "./pages/MainPage";
import ThemeRecs from "./pages/ThemeRecs";
+import ThemeRecsQuestions from "./pages/ThemeRecsQuestions";
+import ThemeRecsResult from "./pages/ThemeRecsResult";
+import Navbar from "./components/Navbar";
const App: React.FC = () => {
return (
+
-
-
+ } />
+ } />
+ }
+ />
+ } />
);
diff --git a/src/components/Navbar.tsx b/src/components/Navbar.tsx
index aaa2c8c..7438a57 100644
--- a/src/components/Navbar.tsx
+++ b/src/components/Navbar.tsx
@@ -1,5 +1,5 @@
import React from "react";
-import { Container } from "../styles/NavbarStyled";
+import { Container, NavLink } from "../styles/NavbarStyled";
import logo from "../image/bangpro_logo.png";
import { BsChatSquareHeartFill } from "react-icons/bs";
import { BsCollectionFill } from "react-icons/bs";
@@ -11,25 +11,34 @@ const Navbar = () => {
+
-
-
-
-
-
);
diff --git a/src/components/RoomTheme.tsx b/src/components/RoomTheme.tsx
new file mode 100644
index 0000000..a34585b
--- /dev/null
+++ b/src/components/RoomTheme.tsx
@@ -0,0 +1,26 @@
+import React from "react";
+import { Theme } from "../styles/RoomThemeStyled";
+
+const RoomTheme = () => {
+ return (
+
+
+
+
+
+ );
+};
+
+export default RoomTheme;
diff --git a/src/index.css b/src/index.css
index 95b2997..d8a4846 100644
--- a/src/index.css
+++ b/src/index.css
@@ -12,3 +12,7 @@ code {
font-family: source-code-pro, Menlo, Monaco, Consolas, "Courier New",
monospace;
}
+
+p {
+ margin: 0;
+}
diff --git a/src/index.tsx b/src/index.tsx
index dac7f10..1264558 100644
--- a/src/index.tsx
+++ b/src/index.tsx
@@ -3,7 +3,6 @@ import ReactDOM from "react-dom/client";
import "./index.css";
import App from "./App";
import reportWebVitals from "./reportWebVitals";
-import Navbar from "./components/Navbar";
const root = ReactDOM.createRoot(
document.getElementById("root") as HTMLElement
@@ -12,7 +11,6 @@ const root = ReactDOM.createRoot(
root.render(
// strictMode에서는 두번 렌더링 될 수 있으나, 프로덕션빌드 모드에서는 그렇지 않을것
-
);
diff --git a/src/pages/Cube.tsx b/src/pages/Cube.tsx
new file mode 100644
index 0000000..c341d30
--- /dev/null
+++ b/src/pages/Cube.tsx
@@ -0,0 +1,142 @@
+import React, { useEffect, useRef, useState } from "react";
+import * as THREE from "three";
+
+const Cube = () => {
+ const mountRef = useRef
(null);
+ const [mouseX, setMouseX] = useState(0);
+ const [mouseY, setMouseY] = useState(0);
+ const [isDragging, setIsDragging] = useState(false);
+ const [previousMouseX, setPreviousMouseX] = useState(0);
+ const [previousMouseY, setPreviousMouseY] = useState(0);
+ const [initialRotation, setInitialRotation] = useState(
+ null
+ );
+ const [initialZoom, setInitialZoom] = useState(5); // 초기 줌 값을 상태로 관리
+
+ useEffect(() => {
+ const mount = mountRef.current;
+
+ // Scene, Camera, Renderer 생성
+ const scene = new THREE.Scene();
+ scene.background = null; // 배경을 투명하게 설정
+
+ const camera = new THREE.PerspectiveCamera(
+ 70,
+ mount!.clientWidth / mount!.clientHeight,
+ 0.1,
+ 1000
+ );
+ const renderer = new THREE.WebGLRenderer({ alpha: true }); // alpha 속성을 true로 설정
+ renderer.setSize(mount!.clientWidth, mount!.clientHeight);
+ mount!.appendChild(renderer.domElement);
+
+ // 조명
+ const directionLight = new THREE.DirectionalLight(0xffffff, 1.5);
+ directionLight.position.set(-0.7, 1, 5);
+ scene.add(directionLight);
+
+ // 큐브 생성 (색상 변경)
+ const geometry = new THREE.BoxGeometry(2, 2, 2);
+ const material = new THREE.MeshPhysicalMaterial({
+ color: 0x03ff8d,
+ metalness: 1,
+ roughness: 0.5,
+ transparent: true,
+ opacity: 0.9,
+ reflectivity: 0.9,
+ });
+ const cube = new THREE.Mesh(geometry, material);
+ scene.add(cube);
+
+ camera.position.z = initialZoom; // 초기 줌 값 설정
+
+ // 마우스 이벤트 리스너
+ const onMouseDown = (event: MouseEvent) => {
+ setIsDragging(true);
+ setPreviousMouseX(event.clientX);
+ setPreviousMouseY(event.clientY);
+ setInitialRotation(cube.rotation.clone()); // 클릭 시점의 회전 상태 저장
+ setInitialZoom(camera.position.z); // 클릭 시점의 줌 값 저장
+ };
+
+ const onMouseMove = (event: MouseEvent) => {
+ if (isDragging) {
+ const deltaX = event.clientX - previousMouseX;
+ const deltaY = event.clientY - previousMouseY;
+
+ if (initialRotation) {
+ cube.rotation.y = initialRotation.y + deltaX * 0.005;
+ cube.rotation.x = initialRotation.x + deltaY * 0.005;
+ }
+
+ setPreviousMouseX(event.clientX);
+ setPreviousMouseY(event.clientY);
+ }
+ };
+
+ const onMouseUp = () => {
+ setIsDragging(false);
+ };
+
+ const onWheel = (event: WheelEvent) => {
+ const delta = event.deltaY * 0.01;
+
+ camera.position.z -= delta;
+
+ // Limiting camera zoom
+ if (camera.position.z < 4) {
+ camera.position.z = 4;
+ } else if (camera.position.z > 15) {
+ camera.position.z = 15;
+ }
+ };
+
+ window.addEventListener("mousedown", onMouseDown);
+ window.addEventListener("mousemove", onMouseMove);
+ window.addEventListener("mouseup", onMouseUp);
+ window.addEventListener("wheel", onWheel);
+
+ if (previousMouseX > 0 || previousMouseY > 0) {
+ cube.rotation.x -= previousMouseX;
+ cube.rotation.y -= previousMouseY;
+ }
+
+ // 반응형 처리
+ const onWindowResize = () => {
+ if (mount) {
+ camera.aspect = mount.clientWidth / mount.clientHeight;
+ camera.updateProjectionMatrix();
+ renderer.setSize(mount.clientWidth, mount.clientHeight);
+ }
+ };
+ window.addEventListener("resize", onWindowResize);
+
+ // 애니메이션 루프
+ const animate = () => {
+ requestAnimationFrame(animate);
+
+ // 기본 회전
+ if (!isDragging) {
+ cube.rotation.x += 0.005;
+ cube.rotation.y += 0.005;
+ }
+
+ renderer.render(scene, camera);
+ };
+ animate();
+
+ // 클린업 함수
+ return () => {
+ mount!.removeChild(renderer.domElement);
+ window.removeEventListener("mousedown", onMouseDown);
+ window.removeEventListener("mousemove", onMouseMove);
+ window.removeEventListener("mouseup", onMouseUp);
+ window.removeEventListener("wheel", onWheel);
+ window.removeEventListener("resize", onWindowResize);
+ };
+ }, [isDragging, initialRotation, initialZoom]);
+
+ return ;
+};
+
+export default Cube;
diff --git a/src/pages/ThemeRecs.tsx b/src/pages/ThemeRecs.tsx
index 11dd954..d44cd02 100644
--- a/src/pages/ThemeRecs.tsx
+++ b/src/pages/ThemeRecs.tsx
@@ -1,10 +1,28 @@
import React from "react";
+import Cube from "./Cube";
+import { Container, StyledLink } from "../styles/ThemeRecsStyled";
+import { IoIosArrowForward } from "react-icons/io";
const ThemeRecs = () => {
return (
- <>
- ThemeRecs
- >
+
+
+
당신을 위한 방,
+
방탈출 맞춤 추천
+
+
+
+
+
+
+
+
);
};
+
export default ThemeRecs;
diff --git a/src/pages/ThemeRecsQuestions.tsx b/src/pages/ThemeRecsQuestions.tsx
new file mode 100644
index 0000000..f515357
--- /dev/null
+++ b/src/pages/ThemeRecsQuestions.tsx
@@ -0,0 +1,136 @@
+import React, { useEffect, useState } from "react";
+import Cube from "./Cube";
+import {
+ Container,
+ StartBtn,
+ Select,
+ StyledLink,
+} from "../styles/ThemeRecsQuestionsStyled";
+import { IoIosArrowForward } from "react-icons/io";
+
+const ThemeRecsQuestions = () => {
+ const [isVisible, setIsVisible] = useState(false);
+
+ const handleScroll = () => {
+ const bottom =
+ window.innerHeight + window.scrollY >=
+ document.documentElement.scrollHeight - 10; // Slight buffer to ensure visibility
+ setIsVisible(bottom);
+ };
+
+ useEffect(() => {
+ window.addEventListener("scroll", handleScroll);
+ return () => {
+ window.removeEventListener("scroll", handleScroll);
+ };
+ }, []);
+
+ return (
+ <>
+
+
+
당신을 위한 방,
+
어느 지역을 원하시나요?
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
당신을 위한 방,
+
어떤 장르를 원하시나요?
+
+
+
+
+
+
+
+
+
+
+
+
+
당신을 위한 방,
+
어떤 난이도를 원하시나요?
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ >
+ );
+};
+
+export default ThemeRecsQuestions;
diff --git a/src/pages/ThemeRecsResult.tsx b/src/pages/ThemeRecsResult.tsx
new file mode 100644
index 0000000..888b9ff
--- /dev/null
+++ b/src/pages/ThemeRecsResult.tsx
@@ -0,0 +1,32 @@
+import React from "react";
+import { Container, StartBtn } from "../styles/ThemeRecsResultStyled";
+import { IoIosArrowForward } from "react-icons/io";
+import RoomTheme from "../components/RoomTheme";
+
+const ThemeRecsResult = () => {
+ return (
+
+
+
방프로가 추천하는
+
당신을 위한 방,
+
+
+
+
+
+
+
+
+
+
+
+
+ );
+};
+
+export default ThemeRecsResult;
diff --git a/src/styles/NavbarStyled.tsx b/src/styles/NavbarStyled.tsx
index 9de6225..e03ecd7 100644
--- a/src/styles/NavbarStyled.tsx
+++ b/src/styles/NavbarStyled.tsx
@@ -13,7 +13,7 @@ export const Container = styled.div`
justify-content: space-between;
background-color: #080101;
- box-shadow: 0px 4px 50px rgba(255, 255, 255, 0.2);
+ box-shadow: 0px 4px 50px rgba(45, 45, 45, 0.126);
color: #3b3b3b;
.logoBox {
@@ -32,35 +32,35 @@ export const Container = styled.div`
justify-content: space-between;
align-items: center;
}
+`;
- .navBox > div {
- display: flex;
- justify-content: center;
- align-items: center;
- }
+export const NavLink = styled(Link)`
+ text-decoration: none;
- .navBox > div > div {
+ .nav {
display: flex;
justify-content: center;
align-items: center;
+ transition: all 0.3s ease-in-out;
+ }
+
+ .nav > div {
font-size: 1.4rem;
margin-right: 0.7rem;
}
- .navBox > div:hover {
+ .nav:hover {
cursor: pointer;
color: white;
+ transform: scale(1.05);
}
-`;
-export const StyledLink = styled(Link)`
- text-decoration: none;
&:focus,
- &:hover,
&:visited,
&:link,
&:active {
text-decoration: none;
+ color: #3b3b3b;
}
`;
diff --git a/src/styles/RoomThemeStyled.tsx b/src/styles/RoomThemeStyled.tsx
new file mode 100644
index 0000000..ea8f397
--- /dev/null
+++ b/src/styles/RoomThemeStyled.tsx
@@ -0,0 +1,68 @@
+import styled from "styled-components";
+import { Link } from "react-router-dom";
+
+export const Theme = styled.div`
+ width: 20vw;
+ height: 20vh;
+ padding: 1rem 2rem;
+ margin: 0 1.5rem;
+
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: flex-start;
+
+ background-color: #080101;
+ color: white;
+ border-radius: 1rem;
+ filter: drop-shadow(0px 0px 7px #03ff8d);
+
+ .titleBox > p {
+ font-size: 1.7rem;
+ border-left: 5px solid #03ff8d;
+ padding-left: 0.5rem;
+ font-weight: bold;
+ }
+
+ .addressBox > p {
+ margin-top: 1rem;
+ font-size: 1.2rem;
+ }
+
+ .cardFooter {
+ width: inherit;
+ margin-top: 1.5rem;
+
+ display: flex;
+ justify-content: space-between;
+ }
+
+ .hashtagBox {
+ display: flex;
+ }
+
+ .hashtagBox > p {
+ margin-right: 1rem;
+ }
+
+ .linkBtn {
+ padding: 0.2rem 1rem;
+ border-radius: 1rem;
+ background-color: #03ff8d;
+ color: #080101;
+
+ cursor: pointer;
+ }
+`;
+
+export const StyledLink = styled(Link)`
+ text-decoration: none;
+
+ &:focus,
+ &:hover,
+ &:visited,
+ &:link,
+ &:active {
+ text-decoration: none;
+ }
+`;
diff --git a/src/styles/ThemeRecsQuestionsStyled.tsx b/src/styles/ThemeRecsQuestionsStyled.tsx
new file mode 100644
index 0000000..4b936e1
--- /dev/null
+++ b/src/styles/ThemeRecsQuestionsStyled.tsx
@@ -0,0 +1,128 @@
+import styled from "styled-components";
+import { Link } from "react-router-dom";
+
+export const Container = styled.div`
+ width: 100vw;
+ height: 90vh;
+ /* margin: 0; */
+ /* margin-top: 10vh; */
+ background: radial-gradient(50% 50% at 50% 50%, #3b3b3b 0%, #080101 100%);
+
+ display: flex;
+
+ .textBox {
+ /* width: 60vw; */
+ height: inherit;
+ padding: 0 10vw;
+
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: flex-start;
+ }
+
+ .subTitle {
+ color: white;
+ font-size: 2.1rem;
+ }
+
+ .title {
+ margin-top: 1rem;
+ color: white;
+ font-size: 3.7rem;
+ font-weight: bold;
+ }
+ .cubeBox {
+ width: 40vw;
+ height: inherit;
+ }
+
+ .selectBox {
+ width: 43vw;
+ margin-top: 2rem;
+ flex-wrap: wrap;
+
+ display: flex;
+ }
+`;
+
+export const StartBtn = styled.div<{ isVisible: boolean }>`
+ // 화면 맨 끝에 도착해야만 보이게
+ transition: opacity 0.7s ease-in-out;
+ opacity: ${(props) => (props.isVisible ? 1 : 0)};
+ transform: ${(props) =>
+ props.isVisible ? "translateY(0)" : "translateY(20px)"};
+ visibility: ${(props) => (props.isVisible ? "visible" : "hidden")};
+
+ .startBtn {
+ position: fixed;
+ bottom: 10vh;
+ right: 10vw;
+
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ height: 2rem;
+ padding: 0.7rem 2rem;
+
+ border-radius: 1rem;
+ border: 3px solid white;
+
+ color: white;
+ background-color: #080101;
+ cursor: pointer;
+ filter: drop-shadow(0px 0px 10px #ffffff62);
+ transition: 0.2s all ease-in-out;
+ }
+
+ .startBtn:hover {
+ filter: drop-shadow(0px 0px 10px #ffffff);
+ }
+
+ .start {
+ font-size: 1.5rem;
+ margin-right: 0.3rem;
+ }
+
+ .icon {
+ width: 1.5rem;
+ font-size: 2rem;
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ }
+`;
+
+export const Select = styled.div`
+ margin-top: 1rem;
+ margin-right: 1rem;
+ padding: 0.5rem 1.3rem;
+ font-size: 1.2rem;
+ color: white;
+ border-radius: 1rem;
+ border: 1px solid white;
+
+ &:hover {
+ cursor: pointer;
+ background-color: rgba(255, 255, 255, 0.2);
+ border-color: #ccc;
+ }
+
+ &:active {
+ background-color: rgba(255, 255, 255, 0.2);
+ border-color: #ccc;
+ }
+`;
+
+export const StyledLink = styled(Link)`
+ text-decoration: none;
+
+ &:focus,
+ &:hover,
+ &:visited,
+ &:link,
+ &:active {
+ text-decoration: none;
+ }
+`;
diff --git a/src/styles/ThemeRecsResultStyled.tsx b/src/styles/ThemeRecsResultStyled.tsx
new file mode 100644
index 0000000..f2c76af
--- /dev/null
+++ b/src/styles/ThemeRecsResultStyled.tsx
@@ -0,0 +1,135 @@
+import styled from "styled-components";
+import { Link } from "react-router-dom";
+
+export const Container = styled.div`
+ width: 100vw;
+ height: 90vh;
+ background: radial-gradient(50% 50% at 50% 50%, #3b3b3b 0%, #080101 100%);
+
+ display: flex;
+ flex-direction: column;
+
+ .textBox {
+ width: 80vw;
+ height: 40vh;
+ padding: 0 10vw;
+
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: flex-start;
+ }
+
+ .subTitle {
+ color: white;
+ font-size: 2.1rem;
+ }
+
+ .title {
+ margin-top: 1rem;
+ color: white;
+ font-size: 3.7rem;
+ font-weight: bold;
+ }
+ .cubeBox {
+ width: 40vw;
+ height: inherit;
+ }
+
+ .selectBox {
+ width: 43vw;
+ margin-top: 2rem;
+ flex-wrap: wrap;
+
+ display: flex;
+ }
+
+ .resultBox {
+ width: 100vw;
+ height: 50vh;
+
+ display: flex;
+ justify-content: center;
+ }
+`;
+
+export const StartBtn = styled.div<{ isVisible: boolean }>`
+ // 화면 맨 끝에 도착해야만 보이게
+ transition: opacity 0.7s ease-in-out;
+ opacity: ${(props) => (props.isVisible ? 1 : 0)};
+ transform: ${(props) =>
+ props.isVisible ? "translateY(0)" : "translateY(20px)"};
+ visibility: ${(props) => (props.isVisible ? "visible" : "hidden")};
+
+ .startBtn {
+ position: fixed;
+ bottom: 10vh;
+ right: 10vw;
+
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ height: 2rem;
+ padding: 0.7rem 2rem;
+
+ border-radius: 1rem;
+ border: 3px solid white;
+
+ color: white;
+ background-color: #080101;
+ cursor: pointer;
+ filter: drop-shadow(0px 0px 10px #ffffff62);
+ transition: 0.2s all ease-in-out;
+ }
+
+ .startBtn:hover {
+ filter: drop-shadow(0px 0px 10px #ffffff);
+ }
+
+ .start {
+ font-size: 1.5rem;
+ margin-right: 0.3rem;
+ }
+
+ .icon {
+ width: 1.5rem;
+ font-size: 2rem;
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ }
+`;
+
+export const Select = styled.div`
+ margin-top: 1rem;
+ margin-right: 1rem;
+ padding: 0.5rem 1.3rem;
+ font-size: 1.2rem;
+ color: white;
+ border-radius: 1rem;
+ border: 1px solid white;
+
+ &:hover {
+ cursor: pointer;
+ background-color: rgba(255, 255, 255, 0.2);
+ border-color: #ccc;
+ }
+
+ &:active {
+ background-color: rgba(255, 255, 255, 0.2);
+ border-color: #ccc;
+ }
+`;
+
+export const StyledLink = styled(Link)`
+ text-decoration: none;
+
+ &:focus,
+ &:hover,
+ &:visited,
+ &:link,
+ &:active {
+ text-decoration: none;
+ }
+`;
diff --git a/src/styles/ThemeRecsStyled.tsx b/src/styles/ThemeRecsStyled.tsx
new file mode 100644
index 0000000..9e1b315
--- /dev/null
+++ b/src/styles/ThemeRecsStyled.tsx
@@ -0,0 +1,90 @@
+import styled from "styled-components";
+import { Link } from "react-router-dom";
+
+export const Container = styled.div`
+ width: 100vw;
+ height: 90vh;
+ /* margin: 0; */
+ margin-top: 10vh;
+ background: radial-gradient(50% 50% at 50% 50%, #3b3b3b 0%, #080101 100%);
+
+ display: flex;
+
+ .textBox {
+ width: 40vw;
+ height: inherit;
+ padding: 0 10vw;
+
+ display: flex;
+ flex-direction: column;
+ justify-content: center;
+ align-items: flex-start;
+ }
+
+ .subTitle {
+ color: white;
+ font-size: 2.3rem;
+ }
+
+ .title {
+ margin-top: 1rem;
+ color: white;
+ font-size: 3.8rem;
+ font-weight: bold;
+ }
+ .cubeBox {
+ width: 50vw;
+ height: inherit;
+ }
+
+ .startBtn {
+ position: absolute;
+ bottom: 10vh;
+ right: 10vw;
+
+ display: flex;
+ justify-content: center;
+ align-items: center;
+
+ height: 2rem;
+ padding: 0.7rem 2rem;
+
+ border-radius: 1rem;
+ border: 3px solid white;
+
+ color: white;
+ background-color: #080101;
+ cursor: pointer;
+ filter: drop-shadow(0px 0px 10px #ffffff62);
+ transition: 0.2s all ease-in-out;
+ }
+
+ .startBtn:hover {
+ filter: drop-shadow(0px 0px 10px #ffffff);
+ }
+
+ .start {
+ font-size: 1.5rem;
+ margin-right: 0.3rem;
+ }
+
+ .icon {
+ width: 1.5rem;
+ font-size: 2rem;
+ display: flex;
+ justify-content: flex-end;
+ align-items: center;
+ }
+`;
+
+export const StyledLink = styled(Link)`
+ text-decoration: none;
+
+ &:focus,
+ &:hover,
+ &:visited,
+ &:link,
+ &:active {
+ text-decoration: none;
+ }
+`;