Initial commit: Unyo documentation website
Add Docusaurus-based documentation site with: - Docker setup for deployment - Documentation pages (intro, installation, getting-started, extensions) - Custom React components (Hero, FeatureGrid, ScreenshotGallery) - Static assets (logos, mascots, screenshots) - Assets directory with promotional images and screenshots Configure .gitignore to exclude: - node_modules - build outputs - IDE files (.idea) - OS files (.DS_Store)
10
.dockerignore
Normal file
@@ -0,0 +1,10 @@
|
||||
node_modules
|
||||
website/node_modules
|
||||
website/.docusaurus
|
||||
website/build
|
||||
.git
|
||||
.github
|
||||
assets
|
||||
.dockerignore
|
||||
docker-compose.yml
|
||||
*.md
|
||||
17
.gitignore
vendored
Normal file
@@ -0,0 +1,17 @@
|
||||
/website/node_modules/
|
||||
/website/build/
|
||||
/website/.docusaurus/
|
||||
|
||||
# IDE
|
||||
.idea/
|
||||
*.iml
|
||||
|
||||
# Logs
|
||||
*.log
|
||||
npm-debug.log*
|
||||
yarn-debug.log*
|
||||
yarn-error.log*
|
||||
|
||||
# OS
|
||||
.DS_Store
|
||||
Thumbs.db
|
||||
BIN
assets/screenshot_anime_details_page.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
assets/screenshot_anime_page_1.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
assets/screenshot_anime_page_2.png
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
assets/screenshot_details_page_2.png
Normal file
|
After Width: | Height: | Size: 448 KiB |
BIN
assets/screenshot_extensions_page.png
Normal file
|
After Width: | Height: | Size: 358 KiB |
BIN
assets/screenshot_home_page.png
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
assets/screenshot_manga_page_1.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
assets/screenshot_settings_1.png
Normal file
|
After Width: | Height: | Size: 355 KiB |
BIN
assets/unyo_logo_image.png
Normal file
|
After Width: | Height: | Size: 826 KiB |
BIN
assets/unyo_mascot_3d_image.png
Normal file
|
After Width: | Height: | Size: 174 KiB |
BIN
assets/unyo_mascot_3d_image_robot.png
Normal file
|
After Width: | Height: | Size: 2.4 MiB |
8
docker-compose.yml
Normal file
@@ -0,0 +1,8 @@
|
||||
services:
|
||||
unyo-web:
|
||||
build:
|
||||
context: .
|
||||
dockerfile: docker/Dockerfile
|
||||
ports:
|
||||
- "80:80"
|
||||
restart: unless-stopped
|
||||
12
docker/Dockerfile
Normal file
@@ -0,0 +1,12 @@
|
||||
FROM node:20-alpine AS build
|
||||
WORKDIR /app
|
||||
COPY website/package*.json ./
|
||||
RUN npm ci
|
||||
COPY website/ ./
|
||||
RUN npm run build
|
||||
|
||||
FROM nginx:alpine
|
||||
COPY docker/nginx.conf /etc/nginx/conf.d/default.conf
|
||||
COPY --from=build /app/build /usr/share/nginx/html
|
||||
EXPOSE 80
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
37
docker/nginx.conf
Normal file
@@ -0,0 +1,37 @@
|
||||
server {
|
||||
listen 80;
|
||||
server_name _;
|
||||
root /usr/share/nginx/html;
|
||||
index index.html;
|
||||
|
||||
# Gzip compression
|
||||
gzip on;
|
||||
gzip_vary on;
|
||||
gzip_proxied any;
|
||||
gzip_comp_level 6;
|
||||
gzip_types text/plain text/css text/xml application/json application/javascript application/xml+rss application/atom+xml image/svg+xml text/x-component text/x-cross-domain-policy;
|
||||
|
||||
# SPA fallback
|
||||
location / {
|
||||
try_files $uri $uri/ /index.html;
|
||||
}
|
||||
|
||||
# Long cache for hashed assets
|
||||
location /assets/ {
|
||||
expires 1y;
|
||||
add_header Cache-Control "public, immutable";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# Short cache for images
|
||||
location /img/ {
|
||||
expires 30d;
|
||||
add_header Cache-Control "public";
|
||||
access_log off;
|
||||
}
|
||||
|
||||
# Security headers
|
||||
add_header X-Frame-Options "SAMEORIGIN" always;
|
||||
add_header X-Content-Type-Options "nosniff" always;
|
||||
add_header Referrer-Policy "strict-origin-when-cross-origin" always;
|
||||
}
|
||||
3
website/babel.config.js
Normal file
@@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
presets: [require.resolve('@docusaurus/core/lib/babel/preset')],
|
||||
};
|
||||
55
website/docs/extensions.md
Normal file
@@ -0,0 +1,55 @@
|
||||
---
|
||||
title: Extensions
|
||||
---
|
||||
|
||||
# Extensions
|
||||
|
||||
Unyo uses the Aniyomi extension ecosystem to provide access to hundreds of anime and manga sources. There are two types of extensions:
|
||||
|
||||
## Remote Extensions
|
||||
|
||||
Remote extensions are powered by a custom backend API that runs [Aniyomi](https://github.com/aniyomiorg/aniyomi) extensions on the server. This means:
|
||||
|
||||
- **No additional setup needed** — they work out of the box.
|
||||
- **Server-dependent** — availability depends on the server's uptime and location.
|
||||
- **Fewer extensions** — not all extensions work in a remote environment.
|
||||
|
||||
If you want a source added, open an issue on the [Unyo GitHub](https://github.com/K3vinb5/Unyo/issues) with the extension name, and it will be evaluated for integration.
|
||||
|
||||
## Local Extensions
|
||||
|
||||
Local extensions run the extension API directly on your computer using a modular system, also based on Aniyomi extensions.
|
||||
|
||||
### Advantages
|
||||
|
||||
- **Faster response times** — no network round-trip to a remote server.
|
||||
- **More extensions** — many extensions that don't work remotely work locally.
|
||||
- **No server dependency** — works even if the remote backend is down.
|
||||
|
||||
### Disadvantages
|
||||
|
||||
- **Requires Java 17+** — you need Java 17 or later installed on your machine.
|
||||
- **More RAM usage** — approximately 100–200 MB additional memory consumption.
|
||||
|
||||
### Java Installation
|
||||
|
||||
If you don't have Java 17+ installed, download it here:
|
||||
|
||||
| Platform | Download |
|
||||
|----------|----------|
|
||||
| Linux | [oracle.com/java/technologies/downloads/#jdk17-linux](https://www.oracle.com/java/technologies/downloads/#jdk17-linux) |
|
||||
| macOS | [oracle.com/java/technologies/downloads/#jdk17-mac](https://www.oracle.com/java/technologies/downloads/#jdk17-mac) |
|
||||
| Windows | [oracle.com/java/technologies/downloads/#jdk17-windows](https://www.oracle.com/java/technologies/downloads/#jdk17-windows) |
|
||||
|
||||
### Installing Local Extensions
|
||||
|
||||
1. Open Unyo and go to **Settings → Extensions**.
|
||||
2. Enable **Local Extensions**.
|
||||
3. Browse available extensions from the [Unyo-Extensions](https://github.com/K3vinb5/Unyo-Extensions) repository directly within the app.
|
||||
4. Install the ones you want.
|
||||
|
||||
## Which Should I Use?
|
||||
|
||||
If you have Java 17+ installed, **local extensions** are recommended for the best experience — faster, more reliable, and more sources available.
|
||||
|
||||
If you prefer a simpler setup or can't install Java, **remote extensions** work great and require no extra configuration.
|
||||
42
website/docs/getting-started.md
Normal file
@@ -0,0 +1,42 @@
|
||||
---
|
||||
title: Getting Started
|
||||
---
|
||||
|
||||
# Getting Started
|
||||
|
||||
Once you've installed Unyo, here's how to get the most out of it.
|
||||
|
||||
## First Launch
|
||||
|
||||
When you open Unyo for the first time, you'll be greeted by the home screen. From here you can browse anime, manga, and manage your watchlist.
|
||||
|
||||
## AniList Login
|
||||
|
||||
Unyo integrates tightly with AniList to sync your watch and read progress.
|
||||
|
||||
1. Open **Settings** from the sidebar.
|
||||
2. Navigate to the **Account** section.
|
||||
3. Click **Login with AniList**.
|
||||
4. Authorize Unyo in your browser.
|
||||
|
||||
:::important
|
||||
Your AniList profile must be set to **Public** or **Semi-Public** for Unyo to pull your anime and manga lists. Private profiles are currently not supported.
|
||||
:::
|
||||
|
||||
## MyAnimeList (MAL) Sync
|
||||
|
||||
Unyo also supports syncing with MyAnimeList. Both services can be active simultaneously — your progress will be synced to whichever tracker you prefer.
|
||||
|
||||
## Browsing Content
|
||||
|
||||
- **Anime** — Browse trending and popular anime, or search for a specific title. Tap any title to see details, episodes, and available sources.
|
||||
- **Manga** — Switch to the manga tab to browse and read manga with the same smooth experience.
|
||||
|
||||
## Settings
|
||||
|
||||
Unyo offers a range of customization options:
|
||||
|
||||
- **Theme** — Switch between light and dark mode.
|
||||
- **Default source** — Set your preferred anime or manga source.
|
||||
- **Notifications** — Enable or disable push notifications for new episodes.
|
||||
- **Extensions** — Manage your remote and local extensions.
|
||||
35
website/docs/installation.md
Normal file
@@ -0,0 +1,35 @@
|
||||
---
|
||||
title: Installation
|
||||
---
|
||||
|
||||
# Installation
|
||||
|
||||
You can download the latest version of Unyo from the [releases page](https://github.com/K3vinb5/Unyo/releases/latest).
|
||||
|
||||
Choose the package that matches your operating system:
|
||||
|
||||
## Windows
|
||||
|
||||
- **Installer (.exe)** — Download the installer, run it, and follow the instructions. This is the recommended method.
|
||||
- **Portable (.zip)** — Download the .zip, extract it, and run `unyo.exe`.
|
||||
|
||||
## macOS
|
||||
|
||||
- **DMG (.dmg)** — Download the .dmg and install it like any other macOS application. **Recommended**.
|
||||
- **Portable (.zip)** — Download the .zip, extract it, and run the .app file.
|
||||
|
||||
## Linux
|
||||
|
||||
There are multiple options available:
|
||||
|
||||
| Method | Instructions |
|
||||
|--------|--------------|
|
||||
| **AM Package Manager** (Recommended) | `am -i unyo` — Also supports updating with `am -u unyo` |
|
||||
| **AUR (Arch)** | Install `unyo-bin` from the AUR |
|
||||
| **AppImage (.AppImage)** | Download and run it |
|
||||
| **DEB package (.deb)** | `sudo apt install ./package_name.deb` (Debian-based only) |
|
||||
| **Portable (.zip)** | Download, extract, and run the `unyo` executable |
|
||||
|
||||
:::tip
|
||||
If you want to use **local extensions**, you need Java 17 or later installed on your machine. See the [Extensions](/docs/extensions) page for details.
|
||||
:::
|
||||
20
website/docs/intro.md
Normal file
@@ -0,0 +1,20 @@
|
||||
---
|
||||
slug: /
|
||||
title: Welcome to Unyo
|
||||
---
|
||||
|
||||
# Welcome to Unyo
|
||||
|
||||
**Unyo** is a desktop application where you can watch anime and read manga — completely ad-free, with automatic AniList & MyAnimeList sync.
|
||||
|
||||

|
||||
|
||||
## Why Unyo?
|
||||
|
||||
- **Ad-Free** — No interruptions, no trackers. Your content, your experience.
|
||||
- **AniList & MAL Sync** — Your watch and read progress syncs automatically.
|
||||
- **Aniyomi Extensions** — Access hundreds of anime and manga sources.
|
||||
- **Cross-Platform** — Available on Linux, Windows, and macOS.
|
||||
- **Open Source** — Fully transparent, GPL-3.0 licensed.
|
||||
|
||||
Ready to get started? Head to the [Installation guide](/docs/installation) to download and set up Unyo.
|
||||
110
website/docusaurus.config.js
Normal file
@@ -0,0 +1,110 @@
|
||||
// @ts-check
|
||||
const {themes} = require('prism-react-renderer');
|
||||
const lightCodeTheme = themes.github;
|
||||
const darkCodeTheme = themes.dracula;
|
||||
|
||||
/** @type {import('@docusaurus/types').Config} */
|
||||
const config = {
|
||||
title: 'Unyo',
|
||||
tagline: 'Simple anime streaming & manga reading, ad-free.',
|
||||
url: 'https://unyo.dev',
|
||||
baseUrl: '/',
|
||||
onBrokenLinks: 'throw',
|
||||
markdown: {
|
||||
format: 'mdx',
|
||||
mdx1Compat: {
|
||||
comments: true,
|
||||
},
|
||||
},
|
||||
favicon: 'img/logo.png',
|
||||
organizationName: 'K3vinb5',
|
||||
projectName: 'Unyo',
|
||||
trailingSlash: false,
|
||||
presets: [
|
||||
[
|
||||
'classic',
|
||||
/** @type {import('@docusaurus/preset-classic').Options} */
|
||||
({
|
||||
docs: {
|
||||
sidebarPath: require.resolve('./sidebars.js'),
|
||||
editUrl: 'https://github.com/K3vinb5/Unyo/tree/rewrite/website',
|
||||
},
|
||||
blog: false,
|
||||
theme: {
|
||||
customCss: require.resolve('./src/css/custom.css'),
|
||||
},
|
||||
}),
|
||||
],
|
||||
],
|
||||
themeConfig:
|
||||
/** @type {import('@docusaurus/preset-classic').ThemeConfig} */
|
||||
({
|
||||
image: 'img/mascot.png',
|
||||
navbar: {
|
||||
title: 'Unyo',
|
||||
logo: {
|
||||
alt: 'Unyo Logo',
|
||||
src: 'img/logo.png',
|
||||
},
|
||||
items: [
|
||||
{
|
||||
to: '/docs',
|
||||
label: 'Docs',
|
||||
position: 'left',
|
||||
},
|
||||
{
|
||||
href: 'https://github.com/K3vinb5/Unyo',
|
||||
label: 'GitHub',
|
||||
position: 'right',
|
||||
},
|
||||
],
|
||||
},
|
||||
footer: {
|
||||
style: 'dark',
|
||||
links: [
|
||||
{
|
||||
title: 'Resources',
|
||||
items: [
|
||||
{
|
||||
label: 'Documentation',
|
||||
to: '/docs',
|
||||
},
|
||||
{
|
||||
label: 'Releases',
|
||||
href: 'https://github.com/K3vinb5/Unyo/releases',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Community',
|
||||
items: [
|
||||
{
|
||||
label: 'GitHub Issues',
|
||||
href: 'https://github.com/K3vinb5/Unyo/issues',
|
||||
},
|
||||
{
|
||||
label: 'Discussions',
|
||||
href: 'https://github.com/K3vinb5/Unyo/discussions',
|
||||
},
|
||||
],
|
||||
},
|
||||
{
|
||||
title: 'Legal',
|
||||
items: [
|
||||
{
|
||||
label: 'GPL-3.0 License',
|
||||
href: 'https://github.com/K3vinb5/Unyo/blob/rewrite/LICENSE',
|
||||
},
|
||||
],
|
||||
},
|
||||
],
|
||||
copyright: `© ${new Date().getFullYear()} Unyo. Built with Docusaurus.`,
|
||||
},
|
||||
prism: {
|
||||
theme: lightCodeTheme,
|
||||
darkTheme: darkCodeTheme,
|
||||
},
|
||||
}),
|
||||
};
|
||||
|
||||
module.exports = config;
|
||||
18448
website/package-lock.json
generated
Normal file
43
website/package.json
Normal file
@@ -0,0 +1,43 @@
|
||||
{
|
||||
"name": "unyo-website",
|
||||
"version": "1.0.0",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"docusaurus": "docusaurus",
|
||||
"start": "docusaurus start",
|
||||
"build": "docusaurus build",
|
||||
"swizzle": "docusaurus swizzle",
|
||||
"deploy": "docusaurus deploy",
|
||||
"clear": "docusaurus clear",
|
||||
"serve": "docusaurus serve",
|
||||
"write-translations": "docusaurus write-translations",
|
||||
"write-heading-ids": "docusaurus write-heading-ids"
|
||||
},
|
||||
"dependencies": {
|
||||
"@docusaurus/core": "^3.10.0",
|
||||
"@docusaurus/preset-classic": "^3.10.0",
|
||||
"@mdx-js/react": "^3.0.0",
|
||||
"clsx": "^2.0.0",
|
||||
"prism-react-renderer": "^2.3.0",
|
||||
"react": "^18.0.0",
|
||||
"react-dom": "^18.0.0",
|
||||
"webpack": "^5.99.0"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@docusaurus/module-type-aliases": "^3.10.0",
|
||||
"@docusaurus/tsconfig": "^3.10.0",
|
||||
"@docusaurus/types": "^3.10.0"
|
||||
},
|
||||
"browserslist": {
|
||||
"production": [
|
||||
">0.5%",
|
||||
"not dead",
|
||||
"not op_mini all"
|
||||
],
|
||||
"development": [
|
||||
"last 3 chrome version",
|
||||
"last 3 firefox version",
|
||||
"last 5 safari version"
|
||||
]
|
||||
}
|
||||
}
|
||||
13
website/sidebars.js
Normal file
@@ -0,0 +1,13 @@
|
||||
// @ts-check
|
||||
|
||||
/** @type {import('@docusaurus/plugin-content-docs').SidebarsConfig} */
|
||||
const sidebars = {
|
||||
docs: [
|
||||
'intro',
|
||||
'installation',
|
||||
'getting-started',
|
||||
'extensions',
|
||||
],
|
||||
};
|
||||
|
||||
module.exports = sidebars;
|
||||
97
website/src/components/FeatureGrid/index.js
Normal file
@@ -0,0 +1,97 @@
|
||||
import React from 'react';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
const features = [
|
||||
{
|
||||
title: 'Anime Streaming',
|
||||
description:
|
||||
'Watch your favorite anime with subtitle support, smooth playback, and torrent streaming.',
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" width="28" height="28">
|
||||
<polygon points="5 3 19 12 5 21 5 3" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Manga Reader',
|
||||
description:
|
||||
'Read manga with a clean, distraction-free reader designed for comfort and immersion.',
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" width="28" height="28">
|
||||
<path d="M2 3h6a4 4 0 0 1 4 4v14a3 3 0 0 0-3-3H2z" />
|
||||
<path d="M22 3h-6a4 4 0 0 0-4 4v14a3 3 0 0 1 3-3h7z" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Ad-Free',
|
||||
description:
|
||||
'No ads, ever. Enjoy your content without interruptions or trackers.',
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" width="28" height="28">
|
||||
<circle cx="12" cy="12" r="10" />
|
||||
<line x1="4.93" y1="4.93" x2="19.07" y2="19.07" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'AniList & MAL Sync',
|
||||
description:
|
||||
'Your watch and read progress syncs automatically with AniList and MyAnimeList.',
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" width="28" height="28">
|
||||
<path d="M10 13a5 5 0 0 0 7.54.54l3-3a5 5 0 0 0-7.07-7.07l-1.72 1.71" />
|
||||
<path d="M14 11a5 5 0 0 0-7.54-.54l-3 3a5 5 0 0 0 7.07 7.07l1.71-1.71" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Aniyomi Extensions',
|
||||
description:
|
||||
'Browse hundreds of sources via the Aniyomi extension ecosystem — both remote and local.',
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" width="28" height="28">
|
||||
<rect x="3" y="3" width="7" height="7" rx="1" />
|
||||
<rect x="14" y="3" width="7" height="7" rx="1" />
|
||||
<rect x="3" y="14" width="7" height="7" rx="1" />
|
||||
<rect x="14" y="14" width="7" height="7" rx="1" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
{
|
||||
title: 'Cross-Platform',
|
||||
description:
|
||||
'Native builds for Linux, Windows, and macOS. One app, every OS.',
|
||||
icon: (
|
||||
<svg viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="1.5" strokeLinecap="round" strokeLinejoin="round" width="28" height="28">
|
||||
<rect x="2" y="3" width="20" height="14" rx="2" ry="2" />
|
||||
<line x1="8" y1="21" x2="16" y2="21" />
|
||||
<line x1="12" y1="17" x2="12" y2="21" />
|
||||
</svg>
|
||||
),
|
||||
},
|
||||
];
|
||||
|
||||
export default function FeatureGrid() {
|
||||
return (
|
||||
<section className={styles.section}>
|
||||
<h2 className={styles.sectionTitle}>
|
||||
Everything you need,{' '}
|
||||
<span className={styles.gradient}>nothing you don't.</span>
|
||||
</h2>
|
||||
<div className={styles.grid}>
|
||||
{features.map((f, i) => (
|
||||
<div
|
||||
key={f.title}
|
||||
className={styles.card}
|
||||
style={{ animationDelay: `${i * 0.08}s` }}
|
||||
>
|
||||
<div className={styles.iconWrap}>{f.icon}</div>
|
||||
<h3 className={styles.cardTitle}>{f.title}</h3>
|
||||
<p className={styles.cardDesc}>{f.description}</p>
|
||||
</div>
|
||||
))}
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
106
website/src/components/FeatureGrid/styles.module.css
Normal file
@@ -0,0 +1,106 @@
|
||||
@keyframes cardAppear {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(24px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
.section {
|
||||
padding: 6rem 2rem;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
font-family: 'Outfit', sans-serif;
|
||||
font-weight: 800;
|
||||
font-size: clamp(1.75rem, 3.5vw, 2.5rem);
|
||||
text-align: center;
|
||||
color: #f5f0ff;
|
||||
margin-bottom: 3.5rem;
|
||||
}
|
||||
|
||||
.gradient {
|
||||
background: linear-gradient(135deg, #a855f7, #ec4899);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, 1fr);
|
||||
gap: 1.5rem;
|
||||
}
|
||||
|
||||
.card {
|
||||
background: rgba(21, 17, 38, 0.6);
|
||||
border: 1px solid rgba(168, 85, 247, 0.1);
|
||||
border-radius: 16px;
|
||||
padding: 2rem;
|
||||
transition: all 0.3s ease;
|
||||
animation: cardAppear 0.6s ease-out both;
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.card:hover {
|
||||
border-color: rgba(168, 85, 247, 0.35);
|
||||
background: rgba(21, 17, 38, 0.85);
|
||||
transform: translateY(-4px);
|
||||
box-shadow: 0 12px 40px rgba(168, 85, 247, 0.15),
|
||||
0 0 0 1px rgba(168, 85, 247, 0.2);
|
||||
}
|
||||
|
||||
.iconWrap {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
width: 48px;
|
||||
height: 48px;
|
||||
border-radius: 12px;
|
||||
background: linear-gradient(135deg, rgba(168, 85, 247, 0.15), rgba(236, 72, 153, 0.15));
|
||||
color: #c084fc;
|
||||
margin-bottom: 1rem;
|
||||
}
|
||||
|
||||
.card:hover .iconWrap {
|
||||
background: linear-gradient(135deg, rgba(168, 85, 247, 0.25), rgba(236, 72, 153, 0.25));
|
||||
color: #f472b6;
|
||||
}
|
||||
|
||||
.cardTitle {
|
||||
font-family: 'Outfit', sans-serif;
|
||||
font-weight: 700;
|
||||
font-size: 1.15rem;
|
||||
color: #f5f0ff;
|
||||
margin: 0 0 0.6rem;
|
||||
}
|
||||
|
||||
.cardDesc {
|
||||
font-family: 'IBM Plex Sans', sans-serif;
|
||||
font-size: 0.9rem;
|
||||
line-height: 1.6;
|
||||
color: #9b8ec4;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
@media (max-width: 996px) {
|
||||
.grid {
|
||||
grid-template-columns: repeat(2, 1fr);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.section {
|
||||
padding: 4rem 1.25rem;
|
||||
}
|
||||
|
||||
.grid {
|
||||
grid-template-columns: 1fr;
|
||||
}
|
||||
}
|
||||
59
website/src/components/Hero/index.js
Normal file
@@ -0,0 +1,59 @@
|
||||
import React from 'react';
|
||||
import clsx from 'clsx';
|
||||
import Link from '@docusaurus/Link';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
export default function Hero() {
|
||||
return (
|
||||
<section className={styles.hero}>
|
||||
<div className={styles.heroGlow} aria-hidden="true" />
|
||||
<div className={styles.heroNoise} aria-hidden="true" />
|
||||
<div className={styles.container}>
|
||||
<div className={styles.heroContent}>
|
||||
<span className={styles.badge}>
|
||||
Open Source • Ad-Free • Multi-Platform
|
||||
</span>
|
||||
<h1 className={styles.heroTitle}>
|
||||
Your anime & manga,{' '}
|
||||
<span className={styles.gradientText}>your way.</span>
|
||||
</h1>
|
||||
<p className={styles.heroSubtitle}>
|
||||
Stream anime, read manga, sync with AniList & MyAnimeList —
|
||||
no ads, no nonsense.
|
||||
</p>
|
||||
<div className={styles.heroCtas}>
|
||||
<Link className={styles.ctaPrimary} to="/docs">
|
||||
Get Started →
|
||||
</Link>
|
||||
<Link
|
||||
className={styles.ctaSecondary}
|
||||
href="https://github.com/K3vinb5/Unyo"
|
||||
>
|
||||
<svg
|
||||
className={styles.githubIcon}
|
||||
viewBox="0 0 24 24"
|
||||
fill="currentColor"
|
||||
width="20"
|
||||
height="20"
|
||||
>
|
||||
<path d="M12 0C5.37 0 0 5.37 0 12c0 5.31 3.435 9.795 8.205 11.385.6.105.825-.255.825-.57 0-.285-.015-1.23-.015-2.235-3.015.555-3.795-.735-4.035-1.41-.135-.345-.72-1.41-1.23-1.695-.42-.225-1.02-.78-.015-.795.945-.015 1.62.87 1.845 1.23 1.08 1.815 2.805 1.305 3.495.99.105-.78.42-1.305.765-1.605-2.67-.3-5.46-1.335-5.46-5.925 0-1.305.465-2.385 1.23-3.225-.12-.3-.54-1.53.12-3.18 0 0 1.005-.315 3.3 1.23.96-.27 1.98-.405 3-.405s2.04.135 3 .405c2.295-1.56 3.3-1.23 3.3-1.23.66 1.65.24 2.88.12 3.18.765.84 1.23 1.905 1.23 3.225 0 4.605-2.805 5.625-5.475 5.925.435.375.81 1.095.81 2.22 0 1.605-.015 2.895-.015 3.3 0 .315.225.69.825.57A12.02 12.02 0 0024 12c0-6.63-5.37-12-12-12z" />
|
||||
</svg>
|
||||
GitHub
|
||||
</Link>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.heroMascotWrap}>
|
||||
<div className={styles.mascotRing} aria-hidden="true" />
|
||||
<img
|
||||
className={styles.heroMascot}
|
||||
src="/img/mascot.png"
|
||||
alt="Unyo mascot"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
<div className={styles.scrollHint} aria-hidden="true">
|
||||
<div className={styles.scrollDot} />
|
||||
</div>
|
||||
</section>
|
||||
);
|
||||
}
|
||||
310
website/src/components/Hero/styles.module.css
Normal file
@@ -0,0 +1,310 @@
|
||||
@keyframes fadeSlideUp {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateY(32px);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateY(0);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes fadeSlideRight {
|
||||
from {
|
||||
opacity: 0;
|
||||
transform: translateX(-32px) scale(0.92);
|
||||
}
|
||||
to {
|
||||
opacity: 1;
|
||||
transform: translateX(0) scale(1);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes mascotFloat {
|
||||
0%, 100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(-14px);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes ringPulse {
|
||||
0%, 100% {
|
||||
opacity: 0.4;
|
||||
transform: scale(1);
|
||||
}
|
||||
50% {
|
||||
opacity: 0.7;
|
||||
transform: scale(1.05);
|
||||
}
|
||||
}
|
||||
|
||||
@keyframes scrollBounce {
|
||||
0%, 100% {
|
||||
transform: translateY(0);
|
||||
}
|
||||
50% {
|
||||
transform: translateY(6px);
|
||||
}
|
||||
}
|
||||
|
||||
.hero {
|
||||
position: relative;
|
||||
min-height: 92vh;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
overflow: hidden;
|
||||
padding: 8rem 2rem 4rem;
|
||||
}
|
||||
|
||||
.heroGlow {
|
||||
position: absolute;
|
||||
top: 10%;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
width: 800px;
|
||||
height: 800px;
|
||||
background: radial-gradient(circle, rgba(168, 85, 247, 0.18) 0%, rgba(236, 72, 153, 0.1) 40%, transparent 70%);
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
}
|
||||
|
||||
.heroNoise {
|
||||
position: absolute;
|
||||
inset: 0;
|
||||
background-image: url("data:image/svg+xml,%3Csvg viewBox='0 0 256 256' xmlns='http://www.w3.org/2000/svg'%3E%3Cfilter id='noise'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.9' numOctaves='4' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='100%25' height='100%25' filter='url(%23noise)' opacity='0.04'/%3E%3C/svg%3E");
|
||||
background-repeat: repeat;
|
||||
pointer-events: none;
|
||||
z-index: 0;
|
||||
opacity: 0.6;
|
||||
}
|
||||
|
||||
.container {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
max-width: 1200px;
|
||||
width: 100%;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: space-between;
|
||||
gap: 4rem;
|
||||
}
|
||||
|
||||
.heroContent {
|
||||
flex: 1;
|
||||
max-width: 600px;
|
||||
animation: fadeSlideUp 0.8s ease-out both;
|
||||
}
|
||||
|
||||
.badge {
|
||||
display: inline-block;
|
||||
padding: 0.4rem 1rem;
|
||||
border-radius: 999px;
|
||||
font-size: 0.8rem;
|
||||
font-weight: 600;
|
||||
font-family: 'Outfit', sans-serif;
|
||||
letter-spacing: 0.04em;
|
||||
text-transform: uppercase;
|
||||
background: rgba(168, 85, 247, 0.1);
|
||||
border: 1px solid rgba(168, 85, 247, 0.25);
|
||||
color: #c084fc;
|
||||
margin-bottom: 1.5rem;
|
||||
}
|
||||
|
||||
.heroTitle {
|
||||
font-family: 'Outfit', sans-serif;
|
||||
font-weight: 900;
|
||||
font-size: clamp(2.5rem, 5vw, 4rem);
|
||||
line-height: 1.1;
|
||||
color: #f5f0ff;
|
||||
margin: 0 0 1.25rem;
|
||||
}
|
||||
|
||||
.gradientText {
|
||||
background: linear-gradient(135deg, #a855f7, #ec4899, #f472b6);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.heroSubtitle {
|
||||
font-family: 'IBM Plex Sans', sans-serif;
|
||||
font-size: 1.15rem;
|
||||
line-height: 1.65;
|
||||
color: #9b8ec4;
|
||||
margin: 0 0 2rem;
|
||||
max-width: 520px;
|
||||
}
|
||||
|
||||
.heroCtas {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
flex-wrap: wrap;
|
||||
}
|
||||
|
||||
.ctaPrimary {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.35rem;
|
||||
padding: 0.85rem 2rem;
|
||||
border-radius: 12px;
|
||||
font-family: 'Outfit', sans-serif;
|
||||
font-size: 1rem;
|
||||
font-weight: 700;
|
||||
text-decoration: none;
|
||||
background: linear-gradient(135deg, #a855f7, #ec4899);
|
||||
color: #fff;
|
||||
box-shadow: 0 4px 24px rgba(168, 85, 247, 0.35), 0 0 0 1px rgba(236, 72, 153, 0.2);
|
||||
transition: all 0.25s ease;
|
||||
}
|
||||
|
||||
.ctaPrimary:hover {
|
||||
background: linear-gradient(135deg, #7c3aed, #f472b6);
|
||||
color: #fff;
|
||||
transform: translateY(-2px);
|
||||
box-shadow: 0 8px 32px rgba(168, 85, 247, 0.45), 0 0 0 1px rgba(236, 72, 153, 0.4);
|
||||
}
|
||||
|
||||
.ctaSecondary {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 0.5rem;
|
||||
padding: 0.85rem 2rem;
|
||||
border-radius: 12px;
|
||||
font-family: 'Outfit', sans-serif;
|
||||
font-size: 1rem;
|
||||
font-weight: 600;
|
||||
text-decoration: none;
|
||||
background: rgba(168, 85, 247, 0.06);
|
||||
border: 1px solid rgba(168, 85, 247, 0.2);
|
||||
color: #c084fc;
|
||||
transition: all 0.25s ease;
|
||||
}
|
||||
|
||||
.ctaSecondary:hover {
|
||||
background: rgba(168, 85, 247, 0.12);
|
||||
border-color: rgba(168, 85, 247, 0.4);
|
||||
color: #f5f0ff;
|
||||
}
|
||||
|
||||
.githubIcon {
|
||||
width: 20px;
|
||||
height: 20px;
|
||||
}
|
||||
|
||||
.heroMascotWrap {
|
||||
position: relative;
|
||||
flex-shrink: 0;
|
||||
animation: fadeSlideRight 0.8s ease-out 0.2s both;
|
||||
}
|
||||
|
||||
.mascotRing {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
left: 50%;
|
||||
transform: translate(-50%, -50%);
|
||||
width: 380px;
|
||||
height: 380px;
|
||||
border-radius: 50%;
|
||||
border: 2px solid rgba(168, 85, 247, 0.15);
|
||||
background: radial-gradient(circle, rgba(168, 85, 247, 0.12) 0%, transparent 70%);
|
||||
animation: ringPulse 4s ease-in-out infinite;
|
||||
}
|
||||
|
||||
.heroMascot {
|
||||
position: relative;
|
||||
z-index: 1;
|
||||
width: 300px;
|
||||
height: 300px;
|
||||
object-fit: contain;
|
||||
animation: mascotFloat 3s ease-in-out infinite;
|
||||
filter: drop-shadow(0 0 40px rgba(168, 85, 247, 0.3));
|
||||
}
|
||||
|
||||
.scrollHint {
|
||||
position: absolute;
|
||||
bottom: 2rem;
|
||||
left: 50%;
|
||||
transform: translateX(-50%);
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.scrollDot {
|
||||
width: 6px;
|
||||
height: 6px;
|
||||
border-radius: 50%;
|
||||
background: rgba(168, 85, 247, 0.5);
|
||||
animation: scrollBounce 2s ease-in-out infinite;
|
||||
}
|
||||
|
||||
/* ====== RESPONSIVE ====== */
|
||||
|
||||
@media (max-width: 996px) {
|
||||
.container {
|
||||
flex-direction: column;
|
||||
text-align: center;
|
||||
gap: 3rem;
|
||||
}
|
||||
|
||||
.heroContent {
|
||||
max-width: 100%;
|
||||
align-items: center;
|
||||
}
|
||||
|
||||
.heroSubtitle {
|
||||
max-width: 100%;
|
||||
}
|
||||
|
||||
.heroCtas {
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.heroMascot {
|
||||
width: 220px;
|
||||
height: 220px;
|
||||
}
|
||||
|
||||
.mascotRing {
|
||||
width: 280px;
|
||||
height: 280px;
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.hero {
|
||||
min-height: 85vh;
|
||||
padding: 6rem 1.25rem 3rem;
|
||||
}
|
||||
|
||||
.heroTitle {
|
||||
font-size: 2.2rem;
|
||||
}
|
||||
|
||||
.heroMascot {
|
||||
width: 180px;
|
||||
height: 180px;
|
||||
}
|
||||
|
||||
.mascotRing {
|
||||
width: 230px;
|
||||
height: 230px;
|
||||
}
|
||||
|
||||
.heroCtas {
|
||||
flex-direction: column;
|
||||
width: 100%;
|
||||
}
|
||||
|
||||
.ctaPrimary,
|
||||
.ctaSecondary {
|
||||
width: 100%;
|
||||
justify-content: center;
|
||||
}
|
||||
}
|
||||
103
website/src/components/ScreenshotGallery/index.js
Normal file
@@ -0,0 +1,103 @@
|
||||
import React, { useRef, useState, useEffect } from 'react';
|
||||
import styles from './styles.module.css';
|
||||
|
||||
const screenshots = [
|
||||
{ src: '/img/screenshots/home.png', alt: 'Unyo home screen' },
|
||||
{ src: '/img/screenshots/anime-1.png', alt: 'Unyo anime browsing' },
|
||||
{ src: '/img/screenshots/anime-2.png', alt: 'Unyo anime playback' },
|
||||
{ src: '/img/screenshots/anime-details.png', alt: 'Unyo anime details' },
|
||||
{ src: '/img/screenshots/manga.png', alt: 'Unyo manga reader' },
|
||||
{ src: '/img/screenshots/details.png', alt: 'Unyo manga details' },
|
||||
{ src: '/img/screenshots/extensions.png', alt: 'Unyo extensions' },
|
||||
{ src: '/img/screenshots/settings.png', alt: 'Unyo settings' },
|
||||
];
|
||||
|
||||
export default function ScreenshotGallery() {
|
||||
const scrollRef = useRef(null);
|
||||
const [canScrollLeft, setCanScrollLeft] = useState(false);
|
||||
const [canScrollRight, setCanScrollRight] = useState(true);
|
||||
const [lightbox, setLightbox] = useState(null);
|
||||
|
||||
const updateScrollButtons = () => {
|
||||
const el = scrollRef.current;
|
||||
if (!el) return;
|
||||
setCanScrollLeft(el.scrollLeft > 2);
|
||||
setCanScrollRight(el.scrollLeft < el.scrollWidth - el.clientWidth - 2);
|
||||
};
|
||||
|
||||
useEffect(() => {
|
||||
const el = scrollRef.current;
|
||||
if (!el) return;
|
||||
updateScrollButtons();
|
||||
el.addEventListener('scroll', updateScrollButtons, { passive: true });
|
||||
window.addEventListener('resize', updateScrollButtons);
|
||||
return () => {
|
||||
el.removeEventListener('scroll', updateScrollButtons);
|
||||
window.removeEventListener('resize', updateScrollButtons);
|
||||
};
|
||||
}, []);
|
||||
|
||||
const scroll = (dir) => {
|
||||
const el = scrollRef.current;
|
||||
if (!el) return;
|
||||
el.scrollBy({ left: dir * 340, behavior: 'smooth' });
|
||||
};
|
||||
|
||||
return (
|
||||
<section className={styles.section}>
|
||||
<h2 className={styles.sectionTitle}>
|
||||
See it in action
|
||||
</h2>
|
||||
<div className={styles.carouselWrap}>
|
||||
{canScrollLeft && (
|
||||
<button
|
||||
className={`${styles.scrollBtn} ${styles.scrollBtnLeft}`}
|
||||
onClick={() => scroll(-1)}
|
||||
aria-label="Scroll left"
|
||||
>
|
||||
‹
|
||||
</button>
|
||||
)}
|
||||
<div className={styles.carousel} ref={scrollRef}>
|
||||
{screenshots.map((s) => (
|
||||
<button
|
||||
key={s.src}
|
||||
className={styles.slide}
|
||||
onClick={() => setLightbox(s)}
|
||||
type="button"
|
||||
>
|
||||
<img src={s.src} alt={s.alt} className={styles.screenshot} />
|
||||
</button>
|
||||
))}
|
||||
</div>
|
||||
{canScrollRight && (
|
||||
<button
|
||||
className={`${styles.scrollBtn} ${styles.scrollBtnRight}`}
|
||||
onClick={() => scroll(1)}
|
||||
aria-label="Scroll right"
|
||||
>
|
||||
›
|
||||
</button>
|
||||
)}
|
||||
<div className={styles.fadeLeft} />
|
||||
<div className={styles.fadeRight} />
|
||||
</div>
|
||||
{lightbox && (
|
||||
<div className={styles.lightbox} onClick={() => setLightbox(null)}>
|
||||
<img
|
||||
src={lightbox.src}
|
||||
alt={lightbox.alt}
|
||||
className={styles.lightboxImg}
|
||||
/>
|
||||
<button
|
||||
className={styles.lightboxClose}
|
||||
onClick={() => setLightbox(null)}
|
||||
aria-label="Close"
|
||||
>
|
||||
×
|
||||
</button>
|
||||
</div>
|
||||
)}
|
||||
</section>
|
||||
);
|
||||
}
|
||||
179
website/src/components/ScreenshotGallery/styles.module.css
Normal file
@@ -0,0 +1,179 @@
|
||||
.section {
|
||||
padding: 4rem 2rem 6rem;
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.sectionTitle {
|
||||
font-family: 'Outfit', sans-serif;
|
||||
font-weight: 800;
|
||||
font-size: clamp(1.75rem, 3.5vw, 2.5rem);
|
||||
text-align: center;
|
||||
color: #f5f0ff;
|
||||
margin-bottom: 3rem;
|
||||
}
|
||||
|
||||
.carouselWrap {
|
||||
position: relative;
|
||||
}
|
||||
|
||||
.carousel {
|
||||
display: flex;
|
||||
gap: 1.25rem;
|
||||
overflow-x: auto;
|
||||
scroll-snap-type: x mandatory;
|
||||
-webkit-overflow-scrolling: touch;
|
||||
scrollbar-width: none;
|
||||
padding: 1rem 0;
|
||||
}
|
||||
|
||||
.carousel::-webkit-scrollbar {
|
||||
display: none;
|
||||
}
|
||||
|
||||
.slide {
|
||||
flex-shrink: 0;
|
||||
scroll-snap-align: start;
|
||||
background: none;
|
||||
border: 2px solid rgba(168, 85, 247, 0.1);
|
||||
border-radius: 16px;
|
||||
padding: 0;
|
||||
overflow: hidden;
|
||||
cursor: pointer;
|
||||
transition: all 0.3s ease;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.slide:hover {
|
||||
border-color: rgba(168, 85, 247, 0.4);
|
||||
box-shadow: 0 8px 32px rgba(168, 85, 247, 0.2);
|
||||
transform: translateY(-4px);
|
||||
}
|
||||
|
||||
.slide:focus-visible {
|
||||
border-color: var(--unyo-pink);
|
||||
}
|
||||
|
||||
.screenshot {
|
||||
display: block;
|
||||
width: 320px;
|
||||
height: auto;
|
||||
border-radius: 14px;
|
||||
}
|
||||
|
||||
.scrollBtn {
|
||||
position: absolute;
|
||||
top: 50%;
|
||||
transform: translateY(-50%);
|
||||
z-index: 3;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
border-radius: 50%;
|
||||
border: 1px solid rgba(168, 85, 247, 0.3);
|
||||
background: rgba(13, 10, 26, 0.9);
|
||||
color: #c084fc;
|
||||
font-size: 1.5rem;
|
||||
line-height: 1;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
.scrollBtn:hover {
|
||||
background: rgba(168, 85, 247, 0.2);
|
||||
border-color: rgba(168, 85, 247, 0.5);
|
||||
}
|
||||
|
||||
.scrollBtnLeft {
|
||||
left: -0.5rem;
|
||||
}
|
||||
|
||||
.scrollBtnRight {
|
||||
right: -0.5rem;
|
||||
}
|
||||
|
||||
.fadeLeft {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
width: 60px;
|
||||
background: linear-gradient(to right, #0d0a1a, transparent);
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.fadeRight {
|
||||
position: absolute;
|
||||
top: 0;
|
||||
right: 0;
|
||||
bottom: 0;
|
||||
width: 60px;
|
||||
background: linear-gradient(to left, #0d0a1a, transparent);
|
||||
pointer-events: none;
|
||||
z-index: 2;
|
||||
}
|
||||
|
||||
.lightbox {
|
||||
position: fixed;
|
||||
inset: 0;
|
||||
z-index: 9999;
|
||||
background: rgba(0, 0, 0, 0.85);
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
cursor: pointer;
|
||||
animation: fadeIn 0.2s ease-out;
|
||||
backdrop-filter: blur(8px);
|
||||
-webkit-backdrop-filter: blur(8px);
|
||||
}
|
||||
|
||||
@keyframes fadeIn {
|
||||
from { opacity: 0; }
|
||||
to { opacity: 1; }
|
||||
}
|
||||
|
||||
.lightboxImg {
|
||||
max-width: 90vw;
|
||||
max-height: 85vh;
|
||||
border-radius: 16px;
|
||||
box-shadow: 0 24px 64px rgba(0, 0, 0, 0.5);
|
||||
}
|
||||
|
||||
.lightboxClose {
|
||||
position: absolute;
|
||||
top: 1.5rem;
|
||||
right: 2rem;
|
||||
background: none;
|
||||
border: 2px solid rgba(255, 255, 255, 0.3);
|
||||
border-radius: 50%;
|
||||
width: 44px;
|
||||
height: 44px;
|
||||
font-size: 1.5rem;
|
||||
color: #fff;
|
||||
cursor: pointer;
|
||||
transition: all 0.2s ease;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.lightboxClose:hover {
|
||||
border-color: rgba(255, 255, 255, 0.6);
|
||||
background: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
@media (max-width: 600px) {
|
||||
.screenshot {
|
||||
width: 260px;
|
||||
}
|
||||
|
||||
.scrollBtn {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
259
website/src/css/custom.css
Normal file
@@ -0,0 +1,259 @@
|
||||
@import url('https://fonts.googleapis.com/css2?family=Outfit:wght@300;400;500;600;700;800;900&family=IBM+Plex+Sans:wght@300;400;500;600;700&display=swap');
|
||||
|
||||
:root {
|
||||
--unyo-bg: #0d0a1a;
|
||||
--unyo-bg-surface: #151126;
|
||||
--unyo-purple: #a855f7;
|
||||
--unyo-purple-dark: #7c3aed;
|
||||
--unyo-purple-darker: #5b21b6;
|
||||
--unyo-pink: #ec4899;
|
||||
--unyo-pink-glow: #f472b6;
|
||||
--unyo-text: #e8e0f0;
|
||||
--unyo-text-muted: #9b8ec4;
|
||||
--unyo-gradient: linear-gradient(135deg, #a855f7, #ec4899);
|
||||
--unyo-gradient-hover: linear-gradient(135deg, #7c3aed, #f472b6);
|
||||
|
||||
--ifm-color-primary: #a855f7;
|
||||
--ifm-color-primary-dark: #9333ea;
|
||||
--ifm-color-primary-darker: #7c3aed;
|
||||
--ifm-color-primary-darkest: #5b21b6;
|
||||
--ifm-color-primary-light: #c084fc;
|
||||
--ifm-color-primary-lighter: #d8b4fe;
|
||||
--ifm-color-primary-lightest: #f3e8ff;
|
||||
|
||||
--ifm-background-color: #0d0a1a;
|
||||
--ifm-background-surface-color: #151126;
|
||||
--ifm-font-color-base: #e8e0f0;
|
||||
--ifm-heading-color: #f5f0ff;
|
||||
--ifm-color-secondary: #1e1838;
|
||||
--ifm-color-success: #22c55e;
|
||||
--ifm-color-info: #38bdf8;
|
||||
--ifm-color-warning: #f59e0b;
|
||||
--ifm-color-danger: #ef4444;
|
||||
|
||||
--ifm-font-family-base: 'IBM Plex Sans', system-ui, -apple-system, sans-serif;
|
||||
--ifm-font-family-heading: 'Outfit', 'IBM Plex Sans', system-ui, sans-serif;
|
||||
--ifm-heading-font-weight: 700;
|
||||
--ifm-hero-background-color: transparent;
|
||||
|
||||
--ifm-link-color: #c084fc;
|
||||
--ifm-link-hover-color: #f472b6;
|
||||
--ifm-link-decoration: none;
|
||||
|
||||
--ifm-navbar-background-color: rgba(13, 10, 26, 0.85);
|
||||
--ifm-navbar-shadow: 0 1px 0 rgba(168, 85, 247, 0.1);
|
||||
--ifm-footer-background-color: #0a0812;
|
||||
--ifm-footer-link-color: var(--unyo-text-muted);
|
||||
|
||||
--ifm-code-background: #1e1838;
|
||||
--ifm-code-border-radius: 8px;
|
||||
--ifm-pre-background: #1e1838;
|
||||
--ifm-pre-border-radius: 12px;
|
||||
|
||||
--docusaurus-highlighted-code-line-bg: rgba(168, 85, 247, 0.15);
|
||||
}
|
||||
|
||||
/* ====== GLOBAL STYLES ====== */
|
||||
|
||||
html {
|
||||
scroll-behavior: smooth;
|
||||
}
|
||||
|
||||
body {
|
||||
background-color: var(--unyo-bg);
|
||||
}
|
||||
|
||||
::selection {
|
||||
background: rgba(168, 85, 247, 0.4);
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
/* ====== NAVBAR ====== */
|
||||
|
||||
.navbar {
|
||||
backdrop-filter: blur(20px);
|
||||
-webkit-backdrop-filter: blur(20px);
|
||||
border-bottom: 1px solid rgba(168, 85, 247, 0.12);
|
||||
transition: background-color 0.3s ease;
|
||||
}
|
||||
|
||||
.navbar__title {
|
||||
font-family: var(--ifm-font-family-heading);
|
||||
font-weight: 800;
|
||||
font-size: 1.3rem;
|
||||
background: var(--unyo-gradient);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.navbar__link {
|
||||
font-weight: 500;
|
||||
color: var(--unyo-text-muted);
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.navbar__link:hover,
|
||||
.navbar__link--active {
|
||||
color: var(--ifm-link-color);
|
||||
}
|
||||
|
||||
.navbar__logo {
|
||||
height: 28px;
|
||||
width: 28px;
|
||||
border-radius: 6px;
|
||||
margin-right: 0.5rem;
|
||||
}
|
||||
|
||||
/* ====== SIDEBAR ====== */
|
||||
|
||||
.theme-doc-sidebar-container {
|
||||
background-color: var(--unyo-bg-surface);
|
||||
border-right: 1px solid rgba(168, 85, 247, 0.08);
|
||||
}
|
||||
|
||||
.menu__link {
|
||||
color: var(--unyo-text-muted);
|
||||
border-radius: 8px;
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.menu__link:hover,
|
||||
.menu__link--active {
|
||||
color: var(--ifm-link-color);
|
||||
background: rgba(168, 85, 247, 0.08);
|
||||
}
|
||||
|
||||
.menu__list-item-collapsible {
|
||||
border-radius: 8px;
|
||||
}
|
||||
|
||||
/* ====== DOCS PAGES ====== */
|
||||
|
||||
.theme-doc-markdown {
|
||||
color: var(--unyo-text);
|
||||
}
|
||||
|
||||
.theme-doc-markdown h1 {
|
||||
font-family: var(--ifm-font-family-heading);
|
||||
background: var(--unyo-gradient);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.theme-doc-markdown h2,
|
||||
.theme-doc-markdown h3,
|
||||
.theme-doc-markdown h4 {
|
||||
color: var(--ifm-heading-color);
|
||||
font-family: var(--ifm-font-family-heading);
|
||||
}
|
||||
|
||||
.theme-doc-markdown a {
|
||||
color: var(--ifm-link-color);
|
||||
background-image: linear-gradient(transparent 0%, transparent 60%, rgba(168, 85, 247, 0.2) 60%);
|
||||
background-size: 0 100%;
|
||||
background-repeat: no-repeat;
|
||||
transition: background-size 0.3s ease;
|
||||
}
|
||||
|
||||
.theme-doc-markdown a:hover {
|
||||
background-size: 100% 100%;
|
||||
}
|
||||
|
||||
.theme-doc-markdown code {
|
||||
background: var(--ifm-code-background);
|
||||
border: 1px solid rgba(168, 85, 247, 0.15);
|
||||
color: #c084fc;
|
||||
font-size: 0.875em;
|
||||
}
|
||||
|
||||
.theme-doc-markdown blockquote {
|
||||
border-left-color: var(--unyo-purple);
|
||||
background: rgba(168, 85, 247, 0.06);
|
||||
color: var(--unyo-text-muted);
|
||||
}
|
||||
|
||||
.theme-doc-markdown table {
|
||||
border-collapse: separate;
|
||||
border-spacing: 0;
|
||||
border: 1px solid rgba(168, 85, 247, 0.15);
|
||||
border-radius: 12px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.theme-doc-markdown th {
|
||||
background: var(--unyo-bg-surface);
|
||||
color: var(--ifm-heading-color);
|
||||
border-bottom-color: rgba(168, 85, 247, 0.2);
|
||||
}
|
||||
|
||||
.theme-doc-markdown td {
|
||||
border-bottom-color: rgba(168, 85, 247, 0.08);
|
||||
}
|
||||
|
||||
/* ====== BREADCRUMB ====== */
|
||||
|
||||
.breadcrumbs__link {
|
||||
color: var(--unyo-text-muted);
|
||||
}
|
||||
|
||||
.breadcrumbs__link:hover {
|
||||
color: var(--ifm-link-color);
|
||||
}
|
||||
|
||||
/* ====== PAGINATION ====== */
|
||||
|
||||
.pagination-nav__link {
|
||||
border: 1px solid rgba(168, 85, 247, 0.15);
|
||||
border-radius: 12px;
|
||||
background: var(--unyo-bg-surface);
|
||||
color: var(--unyo-text);
|
||||
transition: all 0.2s ease;
|
||||
}
|
||||
|
||||
.pagination-nav__link:hover {
|
||||
border-color: var(--unyo-purple);
|
||||
background: rgba(168, 85, 247, 0.08);
|
||||
}
|
||||
|
||||
/* ====== FOOTER ====== */
|
||||
|
||||
.footer {
|
||||
border-top: 1px solid rgba(168, 85, 247, 0.1);
|
||||
}
|
||||
|
||||
.footer__title {
|
||||
font-family: var(--ifm-font-family-heading);
|
||||
color: var(--ifm-heading-color);
|
||||
font-weight: 700;
|
||||
}
|
||||
|
||||
.footer__link-item {
|
||||
color: var(--unyo-text-muted);
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.footer__link-item:hover {
|
||||
color: var(--ifm-link-color);
|
||||
}
|
||||
|
||||
/* ====== SEARCH ====== */
|
||||
|
||||
.DocSearch-Button {
|
||||
border-radius: 12px;
|
||||
background: var(--unyo-bg-surface);
|
||||
border: 1px solid rgba(168, 85, 247, 0.15);
|
||||
}
|
||||
|
||||
/* ====== MOBILE MENU ====== */
|
||||
|
||||
.menu--show {
|
||||
background-color: var(--unyo-bg-surface);
|
||||
}
|
||||
|
||||
/* ====== OVERFLOW UTILITY ====== */
|
||||
|
||||
.overflow-auto {
|
||||
overflow: auto;
|
||||
}
|
||||
63
website/src/pages/index.js
Normal file
@@ -0,0 +1,63 @@
|
||||
import React from 'react';
|
||||
import Layout from '@theme/Layout';
|
||||
import Hero from '@site/src/components/Hero';
|
||||
import FeatureGrid from '@site/src/components/FeatureGrid';
|
||||
import ScreenshotGallery from '@site/src/components/ScreenshotGallery';
|
||||
import styles from './index.module.css';
|
||||
|
||||
export default function Home() {
|
||||
return (
|
||||
<Layout
|
||||
title="Unyo — Anime Streaming & Manga Reader"
|
||||
description="Simple anime streaming and manga reader desktop app. Ad-free, cross-platform, with AniList & MAL sync."
|
||||
>
|
||||
<main className={styles.main}>
|
||||
<Hero />
|
||||
<div className={styles.divider} aria-hidden="true" />
|
||||
<FeatureGrid />
|
||||
<div className={styles.divider} aria-hidden="true" />
|
||||
<ScreenshotGallery />
|
||||
<footer className={styles.landingFooter}>
|
||||
<div className={styles.footerInner}>
|
||||
<div className={styles.footerLogo}>
|
||||
<img src="/img/logo.png" alt="Unyo" className={styles.footerLogoImg} />
|
||||
<span className={styles.footerName}>Unyo</span>
|
||||
</div>
|
||||
<p className={styles.footerTagline}>
|
||||
Simple anime streaming & manga reading, ad-free.
|
||||
</p>
|
||||
<div className={styles.footerLinks}>
|
||||
<a
|
||||
href="https://github.com/K3vinb5/Unyo"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={styles.footerLink}
|
||||
>
|
||||
GitHub
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/K3vinb5/Unyo/releases"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={styles.footerLink}
|
||||
>
|
||||
Releases
|
||||
</a>
|
||||
<a
|
||||
href="https://github.com/K3vinb5/Unyo/blob/rewrite/LICENSE"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
className={styles.footerLink}
|
||||
>
|
||||
GPL-3.0 License
|
||||
</a>
|
||||
</div>
|
||||
<p className={styles.footerCopy}>
|
||||
© {new Date().getFullYear()} Unyo. All rights reserved.
|
||||
</p>
|
||||
</div>
|
||||
</footer>
|
||||
</main>
|
||||
</Layout>
|
||||
);
|
||||
}
|
||||
83
website/src/pages/index.module.css
Normal file
@@ -0,0 +1,83 @@
|
||||
.main {
|
||||
background-color: #0d0a1a;
|
||||
overflow-x: hidden;
|
||||
}
|
||||
|
||||
.divider {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
height: 1px;
|
||||
background: linear-gradient(90deg, transparent, rgba(168, 85, 247, 0.25), transparent);
|
||||
}
|
||||
|
||||
.landingFooter {
|
||||
background: linear-gradient(180deg, #0d0a1a 0%, #0a0812 100%);
|
||||
border-top: 1px solid rgba(168, 85, 247, 0.08);
|
||||
padding: 4rem 2rem 2rem;
|
||||
}
|
||||
|
||||
.footerInner {
|
||||
max-width: 1200px;
|
||||
margin: 0 auto;
|
||||
display: flex;
|
||||
flex-direction: column;
|
||||
align-items: center;
|
||||
gap: 1rem;
|
||||
}
|
||||
|
||||
.footerLogo {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 0.75rem;
|
||||
}
|
||||
|
||||
.footerLogoImg {
|
||||
width: 32px;
|
||||
height: 32px;
|
||||
border-radius: 6px;
|
||||
}
|
||||
|
||||
.footerName {
|
||||
font-family: 'Outfit', sans-serif;
|
||||
font-weight: 800;
|
||||
font-size: 1.3rem;
|
||||
background: linear-gradient(135deg, #a855f7, #ec4899);
|
||||
-webkit-background-clip: text;
|
||||
-webkit-text-fill-color: transparent;
|
||||
background-clip: text;
|
||||
}
|
||||
|
||||
.footerTagline {
|
||||
font-family: 'IBM Plex Sans', sans-serif;
|
||||
font-size: 0.95rem;
|
||||
color: #9b8ec4;
|
||||
text-align: center;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.footerLinks {
|
||||
display: flex;
|
||||
gap: 1.5rem;
|
||||
flex-wrap: wrap;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.footerLink {
|
||||
font-family: 'IBM Plex Sans', sans-serif;
|
||||
font-size: 0.9rem;
|
||||
font-weight: 500;
|
||||
color: #c084fc;
|
||||
text-decoration: none;
|
||||
transition: color 0.2s ease;
|
||||
}
|
||||
|
||||
.footerLink:hover {
|
||||
color: #f472b6;
|
||||
}
|
||||
|
||||
.footerCopy {
|
||||
font-family: 'IBM Plex Sans', sans-serif;
|
||||
font-size: 0.8rem;
|
||||
color: #5b4f7a;
|
||||
margin: 0;
|
||||
}
|
||||
BIN
website/static/img/logo.png
Normal file
|
After Width: | Height: | Size: 826 KiB |
BIN
website/static/img/mascot.png
Normal file
|
After Width: | Height: | Size: 2.4 MiB |
BIN
website/static/img/screenshots/anime-1.png
Normal file
|
After Width: | Height: | Size: 1.3 MiB |
BIN
website/static/img/screenshots/anime-2.png
Normal file
|
After Width: | Height: | Size: 2.2 MiB |
BIN
website/static/img/screenshots/anime-details.png
Normal file
|
After Width: | Height: | Size: 1.1 MiB |
BIN
website/static/img/screenshots/details.png
Normal file
|
After Width: | Height: | Size: 448 KiB |
BIN
website/static/img/screenshots/extensions.png
Normal file
|
After Width: | Height: | Size: 358 KiB |
BIN
website/static/img/screenshots/home.png
Normal file
|
After Width: | Height: | Size: 1.7 MiB |
BIN
website/static/img/screenshots/manga.png
Normal file
|
After Width: | Height: | Size: 1.9 MiB |
BIN
website/static/img/screenshots/settings.png
Normal file
|
After Width: | Height: | Size: 355 KiB |