Leo 3 년 전
부모
커밋
129571580f

+ 4 - 1
.eslintrc.json

@@ -1,3 +1,6 @@
 {
-  "extends": "next/core-web-vitals"
+  "extends": "next/core-web-vitals",
+  "rules": {
+    "@next/next/no-img-element": "off"
+  }
 }

+ 40 - 0
components/NovelCover/index.tsx

@@ -0,0 +1,40 @@
+import clsx from "clsx";
+import type { ElementType } from "react";
+
+interface NovelItemProps {
+  component?: ElementType;
+  className?: string;
+}
+
+export default function NovelItem(props: NovelItemProps) {
+  const { component: Component, className, ...other } = props;
+
+  if (Component) {
+    return (
+      <Component className={clsx("novel-cover", className)} {...other}>
+        <img
+          src="https://www.wuxiaworld.com/cdn-cgi/image/fit=contain,quality=75,format=auto/https://cdn.wuxiaworld.com/images/covers/og.jpg?ver=362f12103bcf1ea3fc048174300b89a65643336b"
+          alt="Minecraft"
+          draggable="false"
+          loading="lazy"
+        />
+      </Component>
+    );
+  }
+
+  return (
+    <a
+      href="/minecraft"
+      title="Minecraft"
+      className={clsx("novel-cover", className)}
+      {...other}
+    >
+      <img
+        src="https://www.wuxiaworld.com/cdn-cgi/image/fit=contain,quality=75,format=auto/https://cdn.wuxiaworld.com/images/covers/og.jpg?ver=362f12103bcf1ea3fc048174300b89a65643336b"
+        alt="Minecraft"
+        draggable="false"
+        loading="lazy"
+      />
+    </a>
+  );
+}

+ 22 - 0
components/NovelItem/index.tsx

@@ -0,0 +1,22 @@
+export default function NovelItem() {
+  return (
+    <li className="novel-item">
+      <a href="/minecraft" title="Minecraft" className="novel-cover">
+        <img
+          src="https://www.wuxiaworld.com/cdn-cgi/image/fit=contain,quality=75,format=auto/https://cdn.wuxiaworld.com/images/covers/og.jpg?ver=362f12103bcf1ea3fc048174300b89a65643336b"
+          alt="Minecraft"
+          draggable="false"
+          loading="lazy"
+        />
+      </a>
+      <h3 className="novel-item-title">
+        <a href="/minecraft" title="Minecraft">
+          Minecraft
+        </a>
+      </h3>
+      <p className="novel-item-desc">
+        <a href="">Fantasy Romance</a>
+      </p>
+    </li>
+  );
+}

+ 3 - 0
components/common/Footer/index.tsx

@@ -0,0 +1,3 @@
+export default function Footer() {
+  return <footer>footer</footer>;
+}

+ 273 - 0
components/common/Header/index.tsx

@@ -0,0 +1,273 @@
+import { useCallback, useState } from "react";
+import clsx from "clsx";
+import Link from "next/link";
+import ClickAwayListener from "@mui/base/ClickAwayListener";
+
+import logo from "../../../public/logo.svg";
+
+export default function Header() {
+  const [open, setOpen] = useState(false);
+
+  const toggleMenu = useCallback(() => {
+    setOpen((o) => !o);
+  }, [setOpen]);
+
+  const closeMenu = useCallback(() => {
+    setOpen(false);
+  }, [setOpen]);
+
+  return (
+    <header className={clsx("header", { open })}>
+      <ClickAwayListener onClickAway={closeMenu}>
+        <div className="container">
+          <Link href="/">
+            <a className="logo" title="NovelDit">
+              {/* eslint-disable-next-line @next/next/no-img-element */}
+              <img src={logo.src} alt="NovelDit" />
+              <span>NovelDit</span>
+            </a>
+          </Link>
+          <div className="menu">
+            <nav>
+              <ul>
+                <li>
+                  <a className="menu-item" href="" title="Browse">
+                    <svg width="24" height="24">
+                      <use href="/icons.svg#browse"></use>
+                    </svg>
+                    <strong>Browse</strong>
+                  </a>
+                  <div className="sub-menu">
+                    <ul className="sub-menu-sized">
+                      <li>
+                        <a href="/stories/novel" title="Novels">
+                          Novels
+                        </a>
+                        <div className="sub-menu-list">
+                          <p>
+                            <strong>MALE LEAD</strong>
+                            <a href="/stories/novel-urban-male" title="Urban">
+                              Urban
+                            </a>
+                            <a
+                              href="/stories/novel-eastern-male"
+                              title="Eastern"
+                            >
+                              Eastern
+                            </a>
+                            <a href="/stories/novel-games-male" title="Games">
+                              Games
+                            </a>
+                            <a
+                              href="/stories/novel-fantasy-male"
+                              title="Fantasy"
+                            >
+                              Fantasy
+                            </a>
+                            <a href="/stories/novel-scifi-male" title="Sci-fi">
+                              Sci-fi
+                            </a>
+                            <a href="/stories/novel-acg-male" title="ACG">
+                              ACG
+                            </a>
+                            <a href="/stories/novel-horror-male" title="Horror">
+                              Horror
+                            </a>
+                            <a href="/stories/novel-sports-male" title="Sports">
+                              Sports
+                            </a>
+                            <a href="/stories/novel-action-male" title="Action">
+                              Action
+                            </a>
+                            <a href="/stories/novel-war-male" title="War">
+                              War
+                            </a>
+                            <a
+                              href="/stories/novel-realistic-male"
+                              title="Realistic"
+                            >
+                              Realistic
+                            </a>
+                            <a
+                              href="/stories/novel-history-male"
+                              title="History"
+                            >
+                              History
+                            </a>
+                          </p>
+                          <p>
+                            <strong>FEMALE LEAD</strong>
+                            <a href="/stories/novel-urban-female" title="Urban">
+                              Urban
+                            </a>
+                            <a
+                              href="/stories/novel-fantasy-female"
+                              title="Fantasy"
+                            >
+                              Fantasy
+                            </a>
+                            <a
+                              href="/stories/novel-history-female"
+                              title="History"
+                            >
+                              History
+                            </a>
+                            <a href="/stories/novel-teen-female" title="Teen">
+                              Teen
+                            </a>
+                            <a href="/stories/novel-lgbt-female" title="LGBT+">
+                              LGBT+
+                            </a>
+                            <a
+                              href="/stories/novel-scifi-female"
+                              title="Sci-fi"
+                            >
+                              Sci-fi
+                            </a>
+                            <a
+                              href="/stories/novel-general-female"
+                              title="General"
+                            >
+                              General
+                            </a>
+                          </p>
+                        </div>
+                      </li>
+                      <li>
+                        <a href="/stories/fanfic" title="Fan-fic">
+                          Fan-fic
+                        </a>
+                        <div className="sub-menu-list">
+                          <p>
+                            <a
+                              href="/stories/fanfic-anime-comics"
+                              title="Anime &amp; Comics"
+                            >
+                              Anime &amp; Comics
+                            </a>
+                            <a
+                              href="/stories/fanfic-video-games"
+                              title="Video Games"
+                            >
+                              Video Games
+                            </a>
+                            <a
+                              href="/stories/fanfic-celebrities"
+                              title="Celebrities"
+                            >
+                              Celebrities
+                            </a>
+                            <a
+                              href="/stories/fanfic-music-bands"
+                              title="Music &amp; Bands"
+                            >
+                              Music &amp; Bands
+                            </a>
+                            <a href="/stories/fanfic-movies" title="Movies">
+                              Movies
+                            </a>
+                            <a
+                              href="/stories/fanfic-book-literature"
+                              title="Book&amp;Literature"
+                            >
+                              Book&amp;Literature
+                            </a>
+                            <a href="/stories/fanfic-tv" title="TV">
+                              TV
+                            </a>
+                            <a href="/stories/fanfic-theater" title="Theater">
+                              Theater
+                            </a>
+                            <a href="/stories/fanfic-others" title="Others">
+                              Others
+                            </a>
+                          </p>
+                        </div>
+                      </li>
+                    </ul>
+                  </div>
+                </li>
+                <li>
+                  <a className="menu-item" href="" title="Rankings">
+                    <svg viewBox="0 0 1024 1024" width="24" height="24">
+                      <use href="/icons.svg#ranking"></use>
+                    </svg>
+                    <strong>Rankings</strong>
+                  </a>
+                  <div className="sub-menu">
+                    <ul>
+                      <li>
+                        <a
+                          href="/ranking/novel/bi_annual/power_rank?timeType=3&amp;sourceType=2&amp;signStatus=1"
+                          title="Novels ranking"
+                        >
+                          Novels ranking
+                        </a>
+                      </li>
+                      <li>
+                        <a
+                          href="/ranking/comic/all_time/comic_power_rank"
+                          title="Comics ranking"
+                        >
+                          Comics ranking
+                        </a>
+                      </li>
+                      <li>
+                        <a
+                          href="/ranking/fanfic/bi_annual/power_rank"
+                          title="Fan-fic ranking"
+                        >
+                          Fan-fic ranking
+                        </a>
+                      </li>
+                    </ul>
+                  </div>
+                </li>
+              </ul>
+            </nav>
+            <form action="./search" className="search-input">
+              <svg className="icon-search">
+                <use href="/icons.svg#search"></use>
+              </svg>
+              <input type="search" name="q" placeholder="Search" />
+            </form>
+          </div>
+          <div className="buttons">
+            <button className="menu-btn" onClick={toggleMenu}>
+              <svg
+                className="icon-menu"
+                xmlns="http://www.w3.org/2000/svg"
+                viewBox="0 0 16 16"
+              >
+                <rect
+                  x="1"
+                  y="4"
+                  width="14"
+                  height="1.5"
+                  rx="1"
+                  className="r-1"
+                ></rect>
+                <rect
+                  x="1"
+                  y="7.25"
+                  width="14"
+                  height="1.5"
+                  rx="1"
+                  className="r-2"
+                ></rect>
+                <rect
+                  x="1"
+                  y="10.5"
+                  width="14"
+                  height="1.5"
+                  rx="1"
+                  className="r-3"
+                ></rect>
+              </svg>
+            </button>
+          </div>
+        </div>
+      </ClickAwayListener>
+    </header>
+  );
+}

+ 13 - 0
components/common/Layout/index.tsx

@@ -0,0 +1,13 @@
+import type { ReactNode } from "react";
+import Header from "../Header";
+import Footer from "../Footer";
+
+export default function Layout({ children }: { children: ReactNode }) {
+  return (
+    <>
+      <Header />
+      {children}
+      <Footer />
+    </>
+  );
+}

+ 3 - 1
package.json

@@ -9,12 +9,14 @@
     "lint": "next lint"
   },
   "dependencies": {
-    "@mui/base": "^5.0.0-alpha.102",
+    "@mui/base": "^5.0.0-alpha.103",
+    "clsx": "^1.2.1",
     "next": "12.3.1",
     "react": "18.2.0",
     "react-dom": "18.2.0"
   },
   "devDependencies": {
+    "@tailwindcss/line-clamp": "^0.4.2",
     "@types/node": "18.11.3",
     "@types/react": "18.0.21",
     "@types/react-dom": "18.0.6",

+ 20 - 5
pages/_app.tsx

@@ -1,8 +1,23 @@
-import '../styles/globals.css'
-import type { AppProps } from 'next/app'
+import type { ReactElement, ReactNode } from "react";
+import type { NextPage } from "next";
+import type { AppProps } from "next/app";
+import Layout from "../components/common/Layout";
+import "../styles/globals.scss";
 
-function MyApp({ Component, pageProps }: AppProps) {
-  return <Component {...pageProps} />
+export type NextPageWithLayout<P = {}, IP = P> = NextPage<P, IP> & {
+  getLayout?: (page: ReactElement) => ReactNode;
+};
+
+type AppPropsWithLayout = AppProps & {
+  Component: NextPageWithLayout;
+};
+
+function defaultGetLayout(page: ReactElement): ReactNode {
+  return <Layout>{page}</Layout>;
 }
 
-export default MyApp
+export default function MyApp({ Component, pageProps }: AppPropsWithLayout) {
+  const getLayout = Component.getLayout ?? defaultGetLayout;
+
+  return getLayout(<Component {...pageProps} />);
+}

+ 19 - 0
pages/_document.js

@@ -0,0 +1,19 @@
+import { Html, Head, Main, NextScript } from "next/document";
+
+export default function Document() {
+  return (
+    <Html>
+      <Head>
+        <link
+          rel="stylesheet"
+          href="https://fonts.googleapis.com/css2?family=Nunito+Sans:ital,wght@0,300;0,400;0,600;0,700;1,400&amp;display=swap"
+          data-ignore="true"
+        />
+      </Head>
+      <body>
+        <Main />
+        <NextScript />
+      </body>
+    </Html>
+  );
+}

+ 13 - 1
pages/index.tsx

@@ -1,7 +1,19 @@
 import type { NextPage } from "next";
+import NovelItem from "../components/NovelItem";
 
 const Home: NextPage = () => {
-  return <div className="bg-black">home</div>;
+  return (
+    <main className="container">
+      <h2 className="novel-title">Popular This Week</h2>
+      <ul className="novel-list">
+        {Array(16)
+          .fill(1)
+          .map((i, idx) => (
+            <NovelItem key={idx} />
+          ))}
+      </ul>
+    </main>
+  );
 };
 
 export default Home;

+ 108 - 0
pages/novel/[slug].tsx

@@ -0,0 +1,108 @@
+import type { NextPage } from "next";
+import Link from "next/link";
+import NovelCover from "../../components/NovelCover";
+import NovelItem from "../../components/NovelItem";
+import styles from "../../styles/novel-info.module.scss";
+
+const Novel: NextPage = () => {
+  return (
+    <main>
+      <div
+        className={styles["novel-wrap"]}
+        style={{
+          backgroundImage:
+            "url(https://www.wuxiaworld.com/cdn-cgi/image/fit=contain,quality=75,format=auto/https://cdn.wuxiaworld.com/images/covers/og.jpg?ver=362f12103bcf1ea3fc048174300b89a65643336b)",
+        }}
+      >
+        <div className={styles["novel-container"]}>
+          <p className={styles["crumbs"]}>
+            <Link href="/">
+              <a title="home" className="">
+                <svg>
+                  <use xlinkHref="/icons.svg#home"></use>
+                </svg>
+              </a>
+            </Link>
+            <span>/</span>
+            <a href="/stories/novel-fantasy-male" className="" title="Fantasy">
+              Fantasy
+            </a>
+            <span>/</span> <span>Supreme Harem God System</span>
+          </p>
+          <div className={styles["novel-info"]}>
+            <NovelCover
+              className={styles["novel-info-cover"]}
+              component="div"
+            />
+            <div className={styles["nove-info-body"]}>
+              <h1>
+                Supreme Harem God System
+                <small>Completed</small>
+              </h1>
+              <h2>
+                <a title="Fantasy" href="/stories/novel-fantasy-male">
+                  <svg>
+                    <use xlinkHref="/icons.svg#paper"></use>
+                  </svg>
+                  <span>Fantasy</span>
+                </a>
+                <strong>
+                  <svg>
+                    <use xlinkHref="/icons.svg#chapter"></use>
+                  </svg>
+                  <span>438 Chapters</span>
+                </strong>
+                <strong>
+                  <svg>
+                    <use xlinkHref="/icons.svg#eye"></use>
+                  </svg>
+                  <span>3.9M Views</span>
+                </strong>
+              </h2>
+              <div className={styles["btns"]}>
+                <a href="" className={styles["button"]}>
+                  Start Reading
+                </a>
+              </div>
+            </div>
+          </div>
+        </div>
+      </div>
+      <div className="container bg-paper py-3">
+        <div className="tabs">
+          <button className="tab active">About</button>
+          <button className="tab">Chapters</button>
+        </div>
+        <h2 className="sub-title">Tags</h2>
+        <div className="tags">
+          {[
+            "Chinese",
+            "Romance",
+            "Fantasy",
+            "Comedy",
+            "Mystery",
+            "Xuanhuan",
+            "Action",
+            "Virtual Reality",
+            "Crafting",
+            "Modern Setting",
+            "Superpowers",
+            "Political Intrigue",
+          ].map((item) => (
+            <a href="" className="tag" key={item}>
+              {item}
+            </a>
+          ))}
+        </div>
+        <h2 className="sub-title">Synopsis</h2>
+        <p>The Age of Gods has long since ended, their voices no longer heard on the continent of Douluo. Humanity, standing on the shoulders of legends from its history, advanced soul technology to inconceivable heights. The humans of Douluo invented weapons of mass destruction, mechanized armor, and living metals. With these advancements, they went on to conquer the oceans and discovered two new continents. To fuel these new technologies, humanity hunted the soul beasts to the very brink of extinction. </p>
+        <p>Once the dominant force of Douluo, the few surviving soul beasts now hide in the darkest recesses of their last sanctuary. The weakest have all been hunted, leaving only the strongest to scheme in the heart of the forest. The soul beasts, not willing to fade away, plot an uprising for their survival. In the midst of this, a god from a long forgotten era awakens in the depths of the Great Star Dou Forest to seek vengeance for the soul beasts. </p>
+        <p>As darkness encroaches from the abyss, hope is found in a young boy who holds a power beyond divinity within himself—Tang Wulin! Hope for both humanity and the soul beasts, as the bridge between the two. He finds his way into the fabled halls of Shrek Academy, where he learns to wield his prodigious powers, makes friends and finds allies, eventually rising to become a towering leader. </p>
+        <p>As he learns to harness his strength, danger lurks within the shadows as the various factions of humanity maneuver and plot to usurp Shrek Academy. Tang Wulin discovers the threat to the only world he knows as he sinks deeper into the intrigue. Meanwhile, camouflaged by the chaos and unbeknownst to the humans, the soul beasts’ plot continues to steadily advance.</p>
+        <p>To defend what he loves and reclaim what was lost, Tang Wulin must seek out the legacies of legends past and unlock the full might of the Golden Dragon King!</p>
+      </div>
+    </main>
+  );
+};
+
+export default Novel;

+ 1 - 5
postcss.config.js

@@ -1,10 +1,6 @@
-// const { join } = require("path");
-
 module.exports = {
   plugins: {
-    tailwindcss: {
-    //   config: join(__dirname, "tailwind.config.js"),
-    },
+    tailwindcss: {},
     autoprefixer: {},
   },
 };

BIN
public/android-chrome-192x192.png


BIN
public/android-chrome-512x512.png


BIN
public/apple-touch-icon.png


BIN
public/favicon-16x16.png


BIN
public/favicon-32x32.png


BIN
public/favicon.ico


+ 31 - 0
public/icons.svg

@@ -0,0 +1,31 @@
+<svg version="1.1"
+    xmlns="http://www.w3.org/2000/svg">
+    <symbol id="search" viewBox="0 0 24 24">
+        <path fill-rule="evenodd" clip-rule="evenodd" d="M14.745 16.442a8 8 0 111.697-1.697L21 19.303 19.303 21l-4.558-4.558zM15.6 10a5.6 5.6 0 11-11.2 0 5.6 5.6 0 0111.2 0z"></path>
+    </symbol>
+    <symbol id="browse" viewBox="0 0 1024 1024">
+        <path d="M512 85.333333c235.52 0 426.666667 191.146667 426.666667 426.666667s-191.146667 426.666667-426.666667 426.666667S85.333333 747.52 85.333333 512 276.48 85.333333 512 85.333333z m234.666667 192L426.24 426.24 277.333333 746.666667l320.426667-148.906667L746.666667 277.333333zM512 465.066667c26.026667 0 46.933333 20.906667 46.933333 46.933333 0 26.026667-20.906667 46.933333-46.933333 46.933333-26.026667 0-46.933333-20.906667-46.933333-46.933333 0-26.026667 20.906667-46.933333 46.933333-46.933333z"></path>
+    </symbol>
+    <symbol id="ranking" viewBox="0 0 1024 1024">
+        <path d="M896 289.706667V168.362667c0-22.186667-24.704-40.405333-54.869333-40.405334H182.869333C152.704 128 128 146.176 128 168.405333V289.706667c0 22.272 24.704 40.448 54.869333 40.448h658.261334c30.165333 0 54.869333-18.176 54.869333-40.448z m-256 565.888V734.293333c0-22.186667-24.704-40.405333-54.869333-40.405333H182.869333c-30.165333 0-54.869333 18.176-54.869333 40.405333v121.301334c0 22.186667 24.704 40.405333 54.869333 40.405333h402.261334c30.165333 0 54.869333-18.176 54.869333-40.405333z m128-282.965334V451.413333c0-22.229333-24.704-40.405333-54.869333-40.405333H182.869333c-30.165333 0-54.869333 18.176-54.869333 40.405333v121.258667c0 22.229333 24.704 40.405333 54.869333 40.405333h530.261334c30.165333 0 54.869333-18.176 54.869333-40.405333z"></path>
+    </symbol>
+    <symbol id="home" viewBox="0 0 1024 1024">
+        <path d="M191.20361087 351.60180543l320.79638913-160.39819456 320.79638913 160.39819456v481.1945837H592.19909729V618.93212991h-160.39819458v213.86425922H191.20361087z"></path>
+    </symbol>
+    <symbol id="paper" viewBox="0 0 1024 1024">
+        <path d="M729.2589302 164.38571167c-47.79696465 0-86.90357209 39.10660744-86.90357208 86.90357209v57.93571449c0 24.62267898-18.82910707 43.45178603-43.45178604 43.45178603H294.7410698v506.93750405l55.0389292-47.79696465c5.79357125-5.79357125 15.93232177-5.79357125 21.72589302 0l43.45178605 39.10660745c5.79357125 5.79357125 15.93232177 5.79357125 21.72589301 0l43.45178604-39.10660745c5.79357125-5.79357125 15.93232177-5.79357125 21.72589303 0l43.45178603 39.10660745c5.79357125 5.79357125 15.93232177 5.79357125 21.72589303 0l43.45178604-39.10660745c5.79357125-5.79357125 15.93232177-5.79357125 21.72589302 0l43.45178604 39.10660745c5.79357125 5.79357125 15.93232177 5.79357125 21.72589301 0l43.45178606-39.10660745c5.79357125-5.79357125 15.93232177-5.79357125 21.72589302 0L816.16250229 859.61428833V251.28928375c0-47.79696465-39.10660744-86.90357209-86.90357209-86.90357208zM374.40267732 425.09642791h188.29107329c11.58714316 0 21.72589302 10.13874985 21.72589302 21.72589303s-10.13874985 21.72589302-21.72589302 21.72589302h-188.29107329c-11.58714316 0-21.72589302-10.13874985-21.72589304-21.72589302s10.13874985-21.72589302 21.72589304-21.72589302z m362.09821745 246.2267878h-362.09821745c-11.58714316 0-21.72589302-10.13874985-21.72589304-21.72589303s10.13874985-21.72589302 21.72589304-21.72589302h362.09821745c11.58714316 0 21.72589302 10.13874985 21.72589301 21.72589302s-10.13874985 21.72589302-21.72589301 21.72589304z m0-101.38750121h-362.09821745c-11.58714316 0-21.72589302-10.13874985-21.72589304-21.72589302s10.13874985-21.72589302 21.72589304-21.72589303h362.09821745c11.58714316 0 21.72589302 10.13874985 21.72589301 21.72589303s-10.13874985 21.72589302-21.72589301 21.72589302z"></path>
+        <path d="M598.90357209 251.28928375c0-33.31303618 13.03553581-63.72928642 33.31303618-86.90357208H294.7410698C246.94410515 164.38571167 207.83749771 203.49231911 207.83749771 251.28928375v57.9357145h391.06607437V251.28928375z"></path>
+    </symbol>
+    <symbol id="chapter" viewBox="0 0 1024 1024">
+        <path d="M192 128h400L832 358.4V896H192V128z m160 153.6v76.8H512V281.6H352z m320 230.4V435.2h-320V512h320z"></path>
+    </symbol>
+    <symbol id="eye" viewBox="0 0 1501 1024">
+        <path d="M1489.76475789 515.08708367c0 21.74380819-6.57683086 41.74274277-16.50918739 59.99680477-151.53555277 263.47589887-429.23888142 440.91611156-721.70652488 440.91611156S181.3780735 837.68735052 29.84252073 575.08388844A125.4295606 125.4295606 0 0 1 13.33333333 515.08708367c0-21.74380819 6.57683086-41.74274277 16.5091874-60.06391453C181.3780735 192.41970706 459.08140216 14.10705758 751.54904562 14.10705758s570.17097211 178.31264948 721.70652488 440.91611156c9.93235655 18.32117175 16.5091874 38.25299558 16.50918739 60.06391453zM751.54904562 846.88149177a335.55259631 335.55259631 0 1 0-2e-8-671.10519362 335.55259631 335.55259631 0 0 0 0 671.10519362z m-2e-8-85.76724417a246.09427467 246.09427467 0 1 1 0-492.12143762 246.09427467 246.09427467 0 0 1 0 492.1214376z"></path>
+    </symbol>
+    <symbol id="sun" viewBox="0 0 16 16">
+        <path fill-rule="evenodd" d="M8 10.5a2.5 2.5 0 100-5 2.5 2.5 0 000 5zM8 12a4 4 0 100-8 4 4 0 000 8zM8 0a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0V.75A.75.75 0 018 0zm0 13a.75.75 0 01.75.75v1.5a.75.75 0 01-1.5 0v-1.5A.75.75 0 018 13zM2.343 2.343a.75.75 0 011.061 0l1.06 1.061a.75.75 0 01-1.06 1.06l-1.06-1.06a.75.75 0 010-1.06zm9.193 9.193a.75.75 0 011.06 0l1.061 1.06a.75.75 0 01-1.06 1.061l-1.061-1.06a.75.75 0 010-1.061zM16 8a.75.75 0 01-.75.75h-1.5a.75.75 0 010-1.5h1.5A.75.75 0 0116 8zM3 8a.75.75 0 01-.75.75H.75a.75.75 0 010-1.5h1.5A.75.75 0 013 8zm10.657-5.657a.75.75 0 010 1.061l-1.061 1.06a.75.75 0 11-1.06-1.06l1.06-1.06a.75.75 0 011.06 0zm-9.193 9.193a.75.75 0 010 1.06l-1.06 1.061a.75.75 0 11-1.061-1.06l1.06-1.061a.75.75 0 011.061 0z"/>
+    </symbol>
+    <symbol id="moon" viewBox="0 0 16 16">
+        <path fill-rule="evenodd" d="M9.598 1.591a.75.75 0 01.785-.175 7 7 0 11-8.967 8.967.75.75 0 01.961-.96 5.5 5.5 0 007.046-7.046.75.75 0 01.175-.786zm1.616 1.945a7 7 0 01-7.678 7.678 5.5 5.5 0 107.678-7.678z"/>
+    </symbol>
+</svg>

+ 1 - 0
public/logo.svg

@@ -0,0 +1 @@
+<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 320 320"><polygon fill="#1e1eff" points="107.77 141.36 70.38 41.14 2.2 74.97 25.29 107.88 25.29 214.84 0 247.75 39.59 266.03 70.38 226.72 70.38 167.3 107.54 266.03 107.54 298.03 152.85 320 152.85 0 107.77 22.86 107.77 141.36"/><path fill="#d2005a" d="M294.71,214.84V107L317.8,75,166.88,0l-.94,320L320,246.83ZM255.12,234l-42.89,21.23V67.65l42.89,20.49L249.62,96V226.72Z"/></svg>

+ 1 - 0
public/site.webmanifest

@@ -0,0 +1 @@
+{"name":"","short_name":"","icons":[{"src":"/android-chrome-192x192.png","sizes":"192x192","type":"image/png"},{"src":"/android-chrome-512x512.png","sizes":"512x512","type":"image/png"}],"theme_color":"#ffffff","background_color":"#ffffff","display":"standalone"}

+ 0 - 4
public/vercel.svg

@@ -1,4 +0,0 @@
-<svg width="283" height="64" viewBox="0 0 283 64" fill="none" 
-    xmlns="http://www.w3.org/2000/svg">
-    <path d="M141.04 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.46 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM248.72 16c-11.04 0-19 7.2-19 18s8.96 18 20 18c6.67 0 12.55-2.64 16.19-7.09l-7.65-4.42c-2.02 2.21-5.09 3.5-8.54 3.5-4.79 0-8.86-2.5-10.37-6.5h28.02c.22-1.12.35-2.28.35-3.5 0-10.79-7.96-17.99-19-17.99zm-9.45 14.5c1.25-3.99 4.67-6.5 9.45-6.5 4.79 0 8.21 2.51 9.45 6.5h-18.9zM200.24 34c0 6 3.92 10 10 10 4.12 0 7.21-1.87 8.8-4.92l7.68 4.43c-3.18 5.3-9.14 8.49-16.48 8.49-11.05 0-19-7.2-19-18s7.96-18 19-18c7.34 0 13.29 3.19 16.48 8.49l-7.68 4.43c-1.59-3.05-4.68-4.92-8.8-4.92-6.07 0-10 4-10 10zm82.48-29v46h-9V5h9zM36.95 0L73.9 64H0L36.95 0zm92.38 5l-27.71 48L73.91 5H84.3l17.32 30 17.32-30h10.39zm58.91 12v9.69c-1-.29-2.06-.49-3.2-.49-5.81 0-10 4-10 10V51h-9V17h9v9.2c0-5.08 5.91-9.2 13.2-9.2z" fill="#000"/>
-</svg>

+ 0 - 3
styles/globals.css

@@ -1,3 +0,0 @@
-@tailwind base;
-@tailwind components;
-@tailwind utilities;

+ 323 - 0
styles/globals.scss

@@ -0,0 +1,323 @@
+@tailwind base;
+@tailwind components;
+@tailwind utilities;
+
+@layer base {
+  *,
+  ::before,
+  ::after {
+    color-scheme: light;
+
+    --color-bg-default: #f3f4f6;
+    --color-paper: #fff;
+    --color-paper-alpha: rgba(255, 255, 255, 0.5);
+
+    // primary
+    --color-primary: #1976d2;
+    // secondary
+    --color-secondary: #9c27b0;
+    // error
+    --color-error: #d32f2f;
+    // warning
+    --color-warning: #ed6c02;
+    // info
+    --color-info: #0288d1;
+    // success
+    --color-success: #2e7d32;
+    --color-gray-light: #808080;
+    --color-gray-dark: #29292e;
+  }
+  .dark *,
+  .dark ::before,
+  .dark ::after {
+    color-scheme: dark;
+
+    --color-bg-default: #18181b;
+    --color-paper: #121212;
+    --color-paper-alpha: rgba(18, 18, 18, 0.5);
+
+    // primary
+    --color-primary: #90caf9;
+    // secondary
+    --color-secondary: #ce93d8;
+    // error
+    --color-error: #f44336;
+    // warning
+    --color-warning: #ffa726;
+    // info
+    --color-info: #29b6f6;
+    // success
+    --color-success: #66bb6a;
+
+    --color-gray-light: #808080;
+    --color-gray-dark: #29292e;
+  }
+
+  * {
+    /* @apply selection:bg-hb; */
+    /* @apply transition selection:bg-hb;
+      transition-timing-function: line; */
+  }
+
+  body {
+    @apply bg-default text-gray-dark font-sans;
+  }
+
+  a {
+    @apply text-gray-dark;
+  }
+}
+@layer components {
+  .container {
+    @apply px-4 mx-auto;
+  }
+}
+#__next {
+  @apply w-full overflow-hidden min-h-screen;
+}
+.header {
+  @apply sticky border-b py-2 shadow bg-paper z-40;
+  .container {
+    @apply flex justify-between items-center;
+  }
+  .logo {
+    @apply flex items-center flex-shrink-0 mr-5 select-none md:mr-10;
+    img {
+      @apply w-10 h-10 mr-2;
+    }
+    span {
+      @apply hidden text-2xl font-bold md:block;
+    }
+  }
+  .menu {
+    @apply hidden absolute left-0 top-full w-full py-5  bg-paper-alpha backdrop-blur;
+    @apply md:flex md:relative md:items-center md:p-0;
+    nav {
+      @apply flex-1;
+      ul {
+        @apply md:flex;
+        li {
+          @apply relative;
+        }
+      }
+    }
+    .menu-item {
+      @apply flex py-2 px-5 items-center text-gray-light;
+      svg {
+        @apply mr-1 fill-current;
+      }
+    }
+    .sub-menu {
+      @apply hidden;
+      @apply absolute bg-default shadow-lg rounded-xl top-full;
+      &::before {
+        content: "";
+        position: absolute;
+        bottom: 100%;
+        left: 25px;
+        border-bottom: 7px solid var(--color-bg-default);
+        border-left: 6px dashed transparent;
+        border-right: 6px dashed transparent;
+        height: 0;
+        width: 0;
+      }
+      ul {
+        @apply py-2 block rounded-l-lg whitespace-nowrap;
+        &.sub-menu-sized {
+          @apply h-[312px] w-[640px];
+        }
+      }
+      .sub-menu-list {
+        @apply absolute left-[132px] hidden top-0 h-[312px] w-[508px] bg-paper rounded-r-lg py-2 px-4;
+        p {
+          @apply flex flex-wrap;
+          strong {
+            @apply block w-full py-1 px-4;
+          }
+          a {
+            @apply w-1/3 block hover:bg-default py-1 px-4 rounded;
+          }
+        }
+      }
+      li {
+        @apply static;
+        > a {
+          @apply block py-3 px-4;
+        }
+        &:hover {
+          @apply bg-paper;
+          .sub-menu-list {
+            @apply block;
+          }
+        }
+        &:first-child {
+          .sub-menu-list {
+            @apply block;
+          }
+        }
+      }
+    }
+    li:hover {
+      .sub-menu {
+        @apply md:block;
+      }
+    }
+  }
+  .search-input {
+    @apply relative mx-5 my-2 md:my-0;
+    .icon-search {
+      @apply absolute left-2 h-full w-5;
+    }
+    input {
+      @apply block w-full border rounded-full pl-10 pr-4 py-1;
+    }
+  }
+  .menu-btn {
+    @apply bg-primary rounded-full p-2 md:hidden;
+    svg {
+      @apply w-5 h-5 fill-white;
+      &.icon-menu {
+        rect {
+          transform: translate(0, 0) rotateZ(0) scaleX(1);
+          transform-origin: center center;
+          transition: all 0.2s ease 0s;
+        }
+      }
+    }
+  }
+  &.open {
+    .menu {
+      @apply block;
+    }
+
+    .menu-btn {
+      svg {
+        &.icon-menu {
+          rect {
+            &.r-1 {
+              transform: translate(1.5px, 2.6px) rotateZ(-45deg);
+            }
+
+            &.r-2 {
+              transform: translate(0, 0) rotateZ(0) scaleX(0);
+            }
+
+            &.r-3 {
+              transform: translate(1.5px, -2.2px) rotateZ(45deg);
+            }
+          }
+        }
+      }
+    }
+  }
+}
+.novel-title {
+  @apply text-xl font-bold mt-5 mb-2 flex justify-between;
+}
+.novel-list {
+  @apply flex flex-wrap -mx-1.5 md:-mx-2 lg:-mx-4;
+
+  .novel-item {
+    @apply w-1/4 px-1.5 my-2;
+    @apply md:px-2 lg:w-1/5 lg:px-4 xl:w-[12.5%];
+  }
+}
+.novel-item {
+  a {
+    color: inherit;
+  }
+}
+.novel-item-title {
+  @apply line-clamp-2 text-xs font-bold leading-none my-1 lg:text-sm;
+}
+.novel-item-desc {
+  @apply text-xs text-gray-light leading-none whitespace-nowrap text-ellipsis block overflow-hidden w-full;
+}
+.novel-cover {
+  @apply block before:block before:pt-[135%] relative;
+  &::after {
+    @apply absolute left-0 top-0 h-full w-full;
+    content: "";
+    // // background-image: repeating-linear-gradient(
+    // //   90deg,
+    // //   rgba(255, 255, 255, 0) 0%,
+    // //   rgba(255, 255, 255, 0.1) 10%,
+    // //   rgba(0, 0, 0, 0.5) 20%,
+    // //   rgba(0, 0, 0, 0.5) 50%,
+    // //   rgba(0, 0, 0, 0.1) 100%,
+    // // );
+    // background-image: linear-gradient(
+    //   90deg,
+    //   rgba(0, 0, 0, 0.2) 0%,
+    //   rgba(255, 255, 255, 0.3) 40%,
+    //   rgba(0, 0, 0, 0.5) 85%,
+    //   rgba(0, 0, 0, 0) 100%
+    // );
+    // background-size: 100% 100%;
+
+    // background-image: linear-gradient(
+    //     to right,
+    //     rgb(60, 13, 20) 0.7712082262210797%,
+    //     rgba(255, 255, 255, 0.5) 1.2853470437017995%,
+    //     rgba(255, 255, 255, 0.25) 1.7994858611825193%,
+    //     rgba(255, 255, 255, 0.25) 2.570694087403599%,
+    //     transparent 3.0848329048843186%,
+    //     transparent 4.113110539845758%,
+    //     rgba(255, 255, 255, 0.25) 4.370179948586118%,
+    //     transparent 5.655526992287918%
+    //   );
+    background-image: linear-gradient(
+        90deg,
+        rgba(0, 0, 0, 0.4) 4px,
+        rgba(0, 0, 0, 0) 7px,
+        rgba(255, 255, 255, 0.35) 8px,
+        rgba(0, 0, 0, 0.3) 9px,
+        rgba(0, 0, 0, 0.3) 12px,
+        rgba(255, 255, 255, 0.25) 13px,
+        transparent 24px
+      ),
+      linear-gradient(
+        310deg,
+        transparent 0%,
+        transparent 70%,
+        rgba(255, 255, 255, 0.4) 100%
+      ),
+      linear-gradient(
+        30deg,
+        rgba(0, 0, 0, 0.6) 0%,
+        transparent 30%,
+        transparent 100%
+      ),
+      linear-gradient(
+        320deg,
+        rgba(0, 0, 0, 0.4) 0%,
+        transparent 30%,
+        transparent 100%
+      );
+    box-shadow: inset -1px 1px 2px rgba(255, 255, 255, 0.5);
+    margin: auto;
+    border-radius: 5px 0 0 5px;
+  }
+  img {
+    @apply absolute left-0 top-0 w-full h-full object-cover;
+    border-radius: 5px 0 0 5px;
+  }
+}
+.tabs {
+  @apply flex justify-start mb-5;
+  .tab {
+    @apply h-11 border-b-4 border-b-transparent font-bold mr-5;
+    &.active {
+      @apply border-b-primary text-primary;
+    }
+  }
+}
+.tags {
+  @apply flex flex-wrap justify-start gap-2;
+}
+.tag {
+  @apply inline-flex items-center cursor-pointer duration-300 text-gray-900  bg-gray-100 rounded px-2.5 py-2 hover:text-primary hover:ring-1 hover:ring-primary;
+}
+.sub-title {
+  @apply text-lg font-bold mt-4 mb-2;
+}

+ 48 - 0
styles/novel-info.module.scss

@@ -0,0 +1,48 @@
+.novel-wrap {
+  @apply bg-cover bg-center overflow-hidden relative;
+  @apply before:absolute before:inset-0 before:bg-white before:bg-opacity-60 before:backdrop-blur;
+}
+.novel-container {
+  @apply container relative;
+}
+.crumbs {
+  @apply flex items-center my-4;
+  svg {
+    @apply w-5 h-5;
+  }
+  a,
+  span {
+    @apply mr-1;
+  }
+}
+.novel-info {
+  @apply flex pb-5;
+  .novel-info-cover {
+    @apply w-24 flex-shrink-0 mr-4 self-start md:w-32 lg:w-56 lg:mr-10;
+  }
+  .nove-info-body {
+    @apply w-0 flex-1 flex flex-col justify-between;
+    h1 {
+      @apply w-full text-xl font-bold md:text-3xl lg:text-4xl;
+      small {
+        @apply text-xs inline-block align-middle px-2 py-0.5 bg-primary text-white rounded ml-2;
+      }
+    }
+    h2 {
+      @apply text-sm flex items-center whitespace-nowrap flex-wrap py-2 lg:text-xl;
+      a,
+      strong {
+        @apply flex items-center mr-2 font-bold lg:mr-4;
+      }
+      svg {
+        @apply w-4 h-4 mr-1 md:w-5 md:h-5 lg:w-8 lg:h-8;
+      }
+    }
+    .btns {
+      @apply flex;
+    }
+    .button {
+      @apply block bg-primary text-white px-4 py-2 rounded-md uppercase lg:text-lg;
+    }
+  }
+}

+ 24 - 5
tailwind.config.js

@@ -1,15 +1,34 @@
 /** @type {import('tailwindcss').Config} */
-// const { join } = require('path');
+const lineClamp = require("@tailwindcss/line-clamp");
 
 module.exports = {
   content: [
-    // join(__dirname, './pages/**/*.{js,ts,jsx,tsx}'),
-    // join(__dirname, './src/**/*.{js,ts,jsx,tsx}'),
     "./pages/**/*.{js,ts,jsx,tsx}",
     "./src/**/*.{js,ts,jsx,tsx}",
+    "./components/**/*.{js,ts,jsx,tsx}",
   ],
+  darkMode: "class",
   theme: {
-    extend: {},
+    extend: {
+      colors: {
+        default: "var(--color-bg-default)",
+        paper: "var(--color-paper)",
+        "paper-alpha": "var(--color-paper-alpha)",
+        primary: "var(--color-primary)",
+        secondary: "var(--color-secondary)",
+        error: "var(--color-error)",
+        warning: "var(--color-warning)",
+        info: "var(--color-info)",
+        success: "var(--color-success)",
+        "gray-light": "var(--color-gray-light)",
+        "gray-dark": "var(--color-gray-dark)",
+      },
+      fontFamily: {
+        sans: [
+          "Nunito Sans,SF Pro Text,SF Pro Icons,Roboto,Helvetica Neue,Helvetica,Arial,sans-serif",
+        ],
+      },
+    },
   },
-  plugins: [],
+  plugins: [lineClamp],
 };

+ 9 - 4
yarn.lock

@@ -63,10 +63,10 @@
   resolved "https://registry.yarnpkg.com/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz#b520529ec21d8e5945a1851dfd1c32e94e39ff45"
   integrity sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==
 
-"@mui/base@^5.0.0-alpha.102":
-  version "5.0.0-alpha.102"
-  resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-alpha.102.tgz#53b07d0b73d3afe1d2a3feb3b43c8188bb821796"
-  integrity sha512-5e/qAIP+DlkrZxIt/cwnDw/A3ii22WkoEoWKHyu4+oeGs3/1Flh7qLaN4h5EAIBB9TvTEZEUzvmsTInmIj6ghg==
+"@mui/base@^5.0.0-alpha.103":
+  version "5.0.0-alpha.103"
+  resolved "https://registry.yarnpkg.com/@mui/base/-/base-5.0.0-alpha.103.tgz#90a91d1eba29ffa0bb243b25e6b1db3a9b4beeda"
+  integrity sha512-fJIyB2df3CHn7D26WHnutnY7vew6aytTlhmRJz6GX7ag19zU2GcOUhJAzY5qwWcrXKnlYgzimhEjaEnuiUWU4g==
   dependencies:
     "@babel/runtime" "^7.19.0"
     "@emotion/is-prop-valid" "^1.2.0"
@@ -208,6 +208,11 @@
   dependencies:
     tslib "^2.4.0"
 
+"@tailwindcss/line-clamp@^0.4.2":
+  version "0.4.2"
+  resolved "https://registry.yarnpkg.com/@tailwindcss/line-clamp/-/line-clamp-0.4.2.tgz#f353c5a8ab2c939c6267ac5b907f012e5ee130f9"
+  integrity sha512-HFzAQuqYCjyy/SX9sLGB1lroPzmcnWv1FHkIpmypte10hptf4oPUfucryMKovZh2u0uiS9U5Ty3GghWfEJGwVw==
+
 "@types/json5@^0.0.29":
   version "0.0.29"
   resolved "https://registry.yarnpkg.com/@types/json5/-/json5-0.0.29.tgz#ee28707ae94e11d2b827bcbe5270bcea7f3e71ee"