feat: add redesign source

This commit is contained in:
TheClashFruit 2024-01-19 22:10:57 +01:00
parent 1f4749c258
commit 94ebea5db1
Signed by: TheClashFruit
GPG key ID: 09BB24C34C2F3204
57 changed files with 5647 additions and 4326 deletions

View file

@ -1,3 +0,0 @@
DB_NAME=
DB_USER=
DB_PASS=

21
.eslintrc.json Normal file
View file

@ -0,0 +1,21 @@
{
"extends": "next/core-web-vitals",
"rules": {
"indent": [
"error",
2
],
"linebreak-style": [
"error",
"unix"
],
"quotes": [
"error",
"single"
],
"semi": [
"error",
"always"
]
}
}

4
.gitignore vendored
View file

@ -4,6 +4,7 @@
/node_modules
/.pnp
.pnp.js
.yarn/install-state.gz
# testing
/coverage
@ -33,3 +34,6 @@ yarn-error.log*
# typescript
*.tsbuildinfo
next-env.d.ts
# IntelliJ
.idea/

View file

@ -2,7 +2,7 @@
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/.idea/newdesignedsite.iml" filepath="$PROJECT_DIR$/.idea/newdesignedsite.iml" />
<module fileurl="file://$PROJECT_DIR$/.idea/site.iml" filepath="$PROJECT_DIR$/.idea/site.iml" />
</modules>
</component>
</project>

View file

@ -1,12 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="WEB_MODULE" version="4">
<component name="NewModuleRootManager">
<content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/.tmp" />
<excludeFolder url="file://$MODULE_DIR$/temp" />
<excludeFolder url="file://$MODULE_DIR$/tmp" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
</component>
</module>

View file

@ -1,4 +1,4 @@
![Website Banner](https://cdn.theclashfruit.me/git/Website/web_banner.svg)
<img alt="Website Banner" src="https://cdn-new.theclashfruit.me/forgejo/website/banner.svg" width="100%">
<h1 align="center">Website</h1>
@ -12,15 +12,15 @@
</p>
<p align="center">
My website written with Next.JS and Tailwind.
My website written with Next.js.
</p>
## Developing
1. Set `DATABASE_CONN` to your MongoDB connection string in `.env.local`.
2. Install dependencies with `npm install`.
3. Run the development server with `npm run dev`.
4. Open [http://localhost:3000](http://localhost:3000) in your browser.
<!-- 1. Set `DATABASE_CONN` to your MongoDB connection string in `.env.local`. -->
1. Install dependencies with `npm install`.
2. Run the development server with `npm run dev`.
3. Open [http://localhost:3000](http://localhost:3000) in your browser.
## Contributing
@ -29,7 +29,7 @@ Feel free to contribute, just make sure you are following design patterns I used
## License
```
Copyright 2023 TheClashFruit
Copyright 2023 - 2024 TheClashFruit
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.

View file

@ -1,23 +0,0 @@
import { useEffect } from 'react';
export default function AdBanner(props) {
useEffect(() => {
try {
(window.adsbygoogle = window.adsbygoogle || []).push({});
} catch (err) {
console.log(err);
}
}, []);
return (
<ins
className="adsbygoogle adbanner-customize"
style={{
display: 'block',
overflow: 'hidden',
}}
data-ad-client="ca-pub-1510964912637528"
{...props}
/>
);
}

View file

@ -1,20 +0,0 @@
import Image from 'next/image';
import Link from 'next/link';
export default function BlogItem({ blogData }) {
return (
<div className={`bg-slate-300 dark:bg-gray-900 rounded-md shadow-lg overflow-clip`}>
<img src={blogData.image} loading="lazy" alt={blogData.title} className={`w-full h-[186px] object-cover`} />
<div className={`pb-2 pt-3 px-3 flex flex-col space-y-0.5`}>
<label className={`text-sm text-opacity-50 dark:text-opacity-50`} suppressHydrationWarning={true}>{new Date(blogData.created * 1000).toLocaleString()}</label>
<Link href={`/post/${blogData.permalink}`} className={`text-2xl truncate font-serif`}>{blogData.title}</Link>
<label className={`text-sm text-opacity-50 dark:text-opacity-50`}>By {blogData.author}</label>
</div>
<div className={`pb-3 px-3`}>
<p>{blogData.content.replace(/(<([^>]+)>)/gi, "").substring(0, 100).trim() + '...'}</p>
</div>
</div>
)
}

21
components/Button.js Normal file
View file

@ -0,0 +1,21 @@
import styles from '@/styles/Components.module.scss';
import Link from 'next/link';
export default function Button({ className, icon: Icon, type, href, children, ...props }) {
if (href) {
return (
<Link href={href} className={className ? `${styles.button} ${className}` : styles.button} data-type={type} {...props}>
{ Icon && <Icon /> }
{ type !== 'icon' && children }
</Link>
);
}
return (
<button className={className ? `${styles.button} ${className}` : styles.button} data-type={type} {...props}>
{ Icon && <Icon /> }
{ type !== 'icon' && children }
</button>
);
}

9
components/Card.js Normal file
View file

@ -0,0 +1,9 @@
import styles from '@/styles/Components.module.scss';
export default function Card({ className, children, ...props }) {
return (
<div className={className ? `${styles.card} ${className}` : styles.card} {...props}>
{ children }
</div>
);
}

View file

@ -0,0 +1,44 @@
import Button from '@/components/Button';
import { Check, X } from 'lucide-react';
import { push } from '@socialgouv/matomo-next';
import styles from '@/styles/Components.module.scss';
export default function ConsentBanner() {
const setCookieConsentGiven = (isAccepted) => {
if (typeof window === 'undefined')
return;
localStorage.setItem('tcf_consent', isAccepted ? 'true' : 'false');
if(isAccepted)
push(['setCookieConsentGiven']);
else
push(['forgetCookieConsentGiven']);
window.dispatchEvent(new Event('consentChange'));
};
return (
<div className={styles.consentBanner}>
<div>
<p>
This site uses Matomo to analyze traffic and help us to improve your user experience.
</p>
<p>
We process your email address and IP address and cookies are stored on your browser for 13 months. This data is only processed by us.
</p>
</div>
<div className={styles.separatedButtonGroup}>
<Button icon={X} type="text" onClick={() => { setCookieConsentGiven(false); }}>
Decline
</Button>
<Button icon={Check} type="primary" onClick={() => { setCookieConsentGiven(true); }}>
Accept
</Button>
</div>
</div>
);
}

22
components/Dialog.js Normal file
View file

@ -0,0 +1,22 @@
import styles from '@/styles/Components.module.scss';
import Button from '@/components/Button';
import { X } from 'lucide-react';
export default function Dialog({ className, title, closeAction, children, ...props }) {
return (
<div className={styles.dialog}>
<div className={className ? `${styles.card} ${className}` : styles.card} {...props}>
<div className={styles.dialogHeader}>
<label>{title}</label>
<Button icon={X} type="icon" onClick={closeAction} />
</div>
<div>
{children}
</div>
</div>
</div>
);
}

View file

@ -1,73 +1,11 @@
import Link from 'next/link';
import styles from '@/styles/Footer.module.scss';
export default function Footer() {
return (
<footer className={`mb-4 max-w-5xl lg:mx-auto max-lg:px-4 lg:px-0 flex max-lg:flex-col max-lg:justify-center max-lg:items-center lg:justify-between items-center`}>
<div className={`max-lg:text-center max-lg:mb-4`}>
<p>Copyright &copy; { new Date().getFullYear() } <Link href="/" className={`underline`}>TheClashFruit</Link>.</p>
<p>This website is <Link href="https://git.theclashfruit.me/TheClashFruit/Website" className={`underline`}>open source</Link>.</p>
</div>
<div>
<ul className={`flex gap-1`}>
<li className={`p-1`}>
<Link href="https://github.com/TheClashFruit" target="_blank" title="GitHub" className={`transition opacity-60 hover:opacity-100`}>
<svg width="28px" height="28px" viewBox="0 0 24 24" className={`accent-white`}>
<path fill="currentColor" d="M12,2A10,10 0 0,0 2,12C2,16.42 4.87,20.17 8.84,21.5C9.34,21.58 9.5,21.27 9.5,21C9.5,20.77 9.5,20.14 9.5,19.31C6.73,19.91 6.14,17.97 6.14,17.97C5.68,16.81 5.03,16.5 5.03,16.5C4.12,15.88 5.1,15.9 5.1,15.9C6.1,15.97 6.63,16.93 6.63,16.93C7.5,18.45 8.97,18 9.54,17.76C9.63,17.11 9.89,16.67 10.17,16.42C7.95,16.17 5.62,15.31 5.62,11.5C5.62,10.39 6,9.5 6.65,8.79C6.55,8.54 6.2,7.5 6.75,6.15C6.75,6.15 7.59,5.88 9.5,7.17C10.29,6.95 11.15,6.84 12,6.84C12.85,6.84 13.71,6.95 14.5,7.17C16.41,5.88 17.25,6.15 17.25,6.15C17.8,7.5 17.45,8.54 17.35,8.79C18,9.5 18.38,10.39 18.38,11.5C18.38,15.32 16.04,16.16 13.81,16.41C14.17,16.72 14.5,17.33 14.5,18.26C14.5,19.6 14.5,20.68 14.5,21C14.5,21.27 14.66,21.59 15.17,21.5C19.14,20.16 22,16.42 22,12A10,10 0 0,0 12,2Z" />
</svg>
</Link>
</li>
<li className={`p-1`}>
<a href="https://git.theclashfruit.me/TheClashFruit" target="_blank" title="Gitea" className={`transition opacity-60 hover:opacity-100`}>
<svg width="28px" height="28px" viewBox="0 0 24 24">
<path fill="currentColor" d="M21.159 6.55586C21.159 6.55586 21.3309 6.55273 21.459 6.68086C21.4624 6.68426 21.4664 6.68805 21.4709 6.69234C21.5498 6.76676 21.789 6.99272 21.8215 8.01211C21.8215 11.5746 20.0965 15.234 20.0965 15.234C19.9715 15.5121 19.8277 15.8059 19.6559 16.109C19.0934 17.0934 18.6902 17.5246 18.6902 17.5246C18.6902 17.5246 18.2996 17.9902 17.8621 18.2559C17.3527 18.5809 16.9777 18.5777 16.9777 18.5777C16.9777 18.5777 13.0434 18.5809 11.0746 18.5809C10.2934 18.484 9.49025 17.6777 8.709 16.6434C8.13087 15.8121 7.75275 14.909 7.75275 14.909C7.75275 14.909 4.6715 14.9496 3.24025 13.1965C2.46525 12.3121 2.30275 11.2965 2.24962 10.9309C2.24815 10.9207 2.24632 10.9087 2.2442 10.8948C2.18365 10.4973 1.89389 8.59488 3.25275 7.38398C3.88712 6.77461 4.66212 6.59961 4.934 6.54336C5.3902 6.44382 5.75345 6.46905 6.04322 6.48917C6.08096 6.49179 6.11746 6.49432 6.15275 6.49648C6.634 6.54023 11.0527 6.76211 11.0527 6.76211C11.0527 6.76211 12.934 6.84336 13.8402 6.83086C13.8402 6.83086 13.8434 9.10586 13.8434 10.2434C13.9293 10.284 14.016 10.3254 14.1027 10.3668C14.1894 10.4082 14.2762 10.4496 14.3621 10.4902V6.82773C14.7669 6.82464 15.1747 6.81545 15.5856 6.80618L15.5996 6.80586C17.4965 6.76211 21.159 6.55586 21.159 6.55586ZM4.7715 12.4746C4.7715 12.4746 5.11525 12.7934 5.92462 13.059C6.45275 13.2465 7.26837 13.3434 7.26837 13.3434C7.26837 13.3434 6.71837 11.8277 6.49337 10.9152C6.22462 9.82773 6.00275 7.97148 6.00275 7.97148C6.00275 7.97148 5.46525 7.95273 5.034 8.06836C4.09337 8.30273 3.83087 9.00586 3.83087 9.00586C3.83087 9.00586 3.359 9.84961 3.87462 11.2309C4.17462 12.0559 4.7715 12.4746 4.7715 12.4746ZM14.6965 16.9027C15.1184 16.8746 15.309 16.4215 15.309 16.4215C15.309 16.4215 16.8309 13.3684 17.0277 12.8871C17.0684 12.7871 17.1434 12.5809 17.084 12.3496C16.9996 12.0215 16.6059 11.8434 16.6059 11.8434L14.2417 10.6965C14.1743 10.8356 14.1068 10.974 14.0394 11.1125C13.9659 11.2633 13.8925 11.4141 13.8191 11.5656C13.8534 11.6031 13.9066 11.6656 13.9409 11.7625C13.9753 11.8562 13.9753 11.9844 13.9753 11.9844C13.9753 11.9844 14.5066 12.2125 14.8878 12.4594C14.8878 12.4594 15.2034 12.625 15.2816 12.9812C15.373 13.3167 15.2324 13.5807 15.1994 13.6426C15.1986 13.6442 15.1979 13.6456 15.1972 13.6469C15.1972 13.6469 14.9316 14.2719 14.7753 14.5969L14.7709 14.606C14.7131 14.7245 14.6582 14.8373 14.6003 14.95C14.7784 15.1469 14.7909 15.4438 14.6347 15.6562C14.4753 15.875 14.1753 15.9469 13.9316 15.8219C13.6878 15.6969 13.5722 15.4094 13.6534 15.1562C13.7284 14.9219 13.9534 14.7594 14.2066 14.7656C14.2066 14.7656 14.7097 13.7344 14.7816 13.4969C14.7816 13.4969 14.9003 13.2031 14.8409 13.0312C14.8003 12.9563 14.7441 12.9 14.6941 12.8594C14.6628 12.8344 14.6347 12.8156 14.6066 12.8C14.4784 12.725 14.3378 12.6469 14.1878 12.5719C14.0441 12.5 13.9066 12.4375 13.7753 12.3813C13.7753 12.3813 13.6816 12.4656 13.5534 12.4937C13.4753 12.5156 13.4066 12.5125 13.3597 12.5094L12.6097 14.0437C12.8003 14.2375 12.8253 14.5437 12.6659 14.7656C12.5034 14.9906 12.1972 15.0594 11.9566 14.9344C11.7347 14.8187 11.6128 14.5594 11.6722 14.3094C11.7316 14.0594 11.9566 13.8813 12.2128 13.8781L12.9816 12.3031C12.8378 12.125 12.8159 11.8812 12.9222 11.6844C13.0222 11.5 13.2159 11.3875 13.4253 11.3906C13.5003 11.2375 13.5761 11.0837 13.6518 10.9298C13.7207 10.7898 13.7897 10.6498 13.858 10.5104L13.1465 10.1652C13.1465 10.1652 12.8371 10.0121 12.584 10.0777C12.5152 10.0934 12.4621 10.1152 12.4434 10.1246C12.2121 10.2277 12.0621 10.5309 12.0621 10.5309L10.3652 14.0277C10.3652 14.0277 10.2121 14.3402 10.2809 14.5934C10.3402 14.9027 10.6809 15.0809 10.6809 15.0809L14.209 16.7996C14.3652 16.8621 14.3746 16.8652 14.3746 16.8652C14.3746 16.8652 14.5152 16.9152 14.6965 16.9027Z" />
</svg>
</a>
</li>
<li className={`p-1`}>
<a href="https://mas.to/@TheClashFruit" target="_blank" title="Mastodon" className={`transition opacity-60 hover:opacity-100`}>
<svg width="28px" height="28px" viewBox="0 0 24 24">
<path fill="currentColor" d="M20.94,14C20.66,15.41 18.5,16.96 15.97,17.26C14.66,17.41 13.37,17.56 12,17.5C9.75,17.39 8,16.96 8,16.96V17.58C8.32,19.8 10.22,19.93 12.03,20C13.85,20.05 15.47,19.54 15.47,19.54L15.55,21.19C15.55,21.19 14.27,21.87 12,22C10.75,22.07 9.19,21.97 7.38,21.5C3.46,20.45 2.78,16.26 2.68,12L2.67,8.57C2.67,4.23 5.5,2.96 5.5,2.96C6.95,2.3 9.41,2 11.97,2H12.03C14.59,2 17.05,2.3 18.5,2.96C18.5,2.96 21.33,4.23 21.33,8.57C21.33,8.57 21.37,11.78 20.94,14M18,8.91C18,7.83 17.7,7 17.15,6.35C16.59,5.72 15.85,5.39 14.92,5.39C13.86,5.39 13.05,5.8 12.5,6.62L12,7.5L11.5,6.62C10.94,5.8 10.14,5.39 9.07,5.39C8.15,5.39 7.41,5.72 6.84,6.35C6.29,7 6,7.83 6,8.91V14.17H8.1V9.06C8.1,8 8.55,7.44 9.46,7.44C10.46,7.44 10.96,8.09 10.96,9.37V12.16H13.03V9.37C13.03,8.09 13.53,7.44 14.54,7.44C15.44,7.44 15.89,8 15.89,9.06V14.17H18V8.91Z" />
</svg>
</a>
</li>
<li className={`p-1`}>
<a href="https://twitter.com/@TheClashFruit" target="_blank" title="Twitter" className={`transition opacity-60 hover:opacity-100`}>
<svg width="28px" height="28px" viewBox="0 0 24 24">
<path fill="currentColor" d="M22.46,6C21.69,6.35 20.86,6.58 20,6.69C20.88,6.16 21.56,5.32 21.88,4.31C21.05,4.81 20.13,5.16 19.16,5.36C18.37,4.5 17.26,4 16,4C13.65,4 11.73,5.92 11.73,8.29C11.73,8.63 11.77,8.96 11.84,9.27C8.28,9.09 5.11,7.38 3,4.79C2.63,5.42 2.42,6.16 2.42,6.94C2.42,8.43 3.17,9.75 4.33,10.5C3.62,10.5 2.96,10.3 2.38,10C2.38,10 2.38,10 2.38,10.03C2.38,12.11 3.86,13.85 5.82,14.24C5.46,14.34 5.08,14.39 4.69,14.39C4.42,14.39 4.15,14.36 3.89,14.31C4.43,16 6,17.26 7.89,17.29C6.43,18.45 4.58,19.13 2.56,19.13C2.22,19.13 1.88,19.11 1.54,19.07C3.44,20.29 5.7,21 8.12,21C16,21 20.33,14.46 20.33,8.79C20.33,8.6 20.33,8.42 20.32,8.23C21.16,7.63 21.88,6.87 22.46,6Z" />
</svg>
</a>
</li>
<li className={`p-1`}>
<a href="https://youtube.com/@TheClashFruit" target="_blank" title="YouTube" className={`transition opacity-60 hover:opacity-100`}>
<svg width="28px" height="28px" viewBox="0 0 24 24">
<path fill="currentColor" d="M10,15L15.19,12L10,9V15M21.56,7.17C21.69,7.64 21.78,8.27 21.84,9.07C21.91,9.87 21.94,10.56 21.94,11.16L22,12C22,14.19 21.84,15.8 21.56,16.83C21.31,17.73 20.73,18.31 19.83,18.56C19.36,18.69 18.5,18.78 17.18,18.84C15.88,18.91 14.69,18.94 13.59,18.94L12,19C7.81,19 5.2,18.84 4.17,18.56C3.27,18.31 2.69,17.73 2.44,16.83C2.31,16.36 2.22,15.73 2.16,14.93C2.09,14.13 2.06,13.44 2.06,12.84L2,12C2,9.81 2.16,8.2 2.44,7.17C2.69,6.27 3.27,5.69 4.17,5.44C4.64,5.31 5.5,5.22 6.82,5.16C8.12,5.09 9.31,5.06 10.41,5.06L12,5C16.19,5 18.8,5.16 19.83,5.44C20.73,5.69 21.31,6.27 21.56,7.17Z" />
</svg>
</a>
</li>
<li className={`p-1`}>
<a href="https://twitch.tv/TheClashFruit" target="_blank" title="Twitch" className={`transition opacity-60 hover:opacity-100`}>
<svg width="28px" height="28px" viewBox="0 0 24 24">
<path fill="currentColor" d="M11.64 5.93H13.07V10.21H11.64M15.57 5.93H17V10.21H15.57M7 2L3.43 5.57V18.43H7.71V22L11.29 18.43H14.14L20.57 12V2M19.14 11.29L16.29 14.14H13.43L10.93 16.64V14.14H7.71V3.43H19.14Z" />
</svg>
</a>
</li>
<li className={`p-1`}>
<a href="https://discord.gg/CWEApqJ6rc" target="_blank" title="Discord" className={`transition opacity-60 hover:opacity-100`}>
<svg width="28px" height="28px" viewBox="0 0 24 24">
<path fill="currentColor" d="M19.6361 5.39641C18.1907 4.73466 16.6648 4.26519 15.0974 4C14.8829 4.38343 14.6888 4.77793 14.516 5.18185C12.8463 4.93025 11.1484 4.93025 9.47883 5.18185C9.30589 4.77797 9.11182 4.38348 8.89742 4C7.32898 4.26743 5.80206 4.73802 4.35518 5.39987C1.48276 9.64968 0.704089 13.7939 1.09342 17.8793C2.77559 19.1222 4.65841 20.0674 6.66004 20.6739C7.11075 20.0677 7.50957 19.4246 7.85227 18.7515C7.20136 18.5083 6.57312 18.2084 5.97481 17.8551C6.13228 17.7409 6.28628 17.6232 6.43509 17.509C8.17602 18.3278 10.0762 18.7523 12 18.7523C13.9238 18.7523 15.8239 18.3278 17.5649 17.509C17.7154 17.6319 17.8694 17.7496 18.0252 17.8551C17.4257 18.209 16.7963 18.5095 16.1442 18.7532C16.4865 19.426 16.8854 20.0686 17.3365 20.6739C19.3398 20.0699 21.2241 19.1251 22.9065 17.8811C23.3634 13.1433 22.1261 9.03712 19.6361 5.39641ZM8.34543 15.3668C7.26048 15.3668 6.36415 14.3823 6.36415 13.171C6.36415 11.9597 7.22934 10.9665 8.34197 10.9665C9.4546 10.9665 10.344 11.9597 10.325 13.171C10.3059 14.3823 9.45114 15.3668 8.34543 15.3668ZM15.6545 15.3668C14.5679 15.3668 13.675 14.3823 13.675 13.171C13.675 11.9597 14.5402 10.9665 15.6545 10.9665C16.7689 10.9665 17.6514 11.9597 17.6324 13.171C17.6133 14.3823 16.7602 15.3668 15.6545 15.3668Z" />
</svg>
</a>
</li>
<li className={`p-1`}>
<a href="/rss.xml" target="_blank" title="RSS" className={`transition opacity-60 hover:opacity-100`}>
<svg width="28px" height="28px" viewBox="0 0 24 24">
<path fill="currentColor" d="M6.18,15.64A2.18,2.18 0 0,1 8.36,17.82C8.36,19 7.38,20 6.18,20C5,20 4,19 4,17.82A2.18,2.18 0 0,1 6.18,15.64M4,4.44A15.56,15.56 0 0,1 19.56,20H16.73A12.73,12.73 0 0,0 4,7.27V4.44M4,10.1A9.9,9.9 0 0,1 13.9,20H11.07A7.07,7.07 0 0,0 4,12.93V10.1Z" />
</svg>
</a>
</li>
</ul>
<footer className={styles.footer}>
<div className={styles.container}>
<p>Copyright &copy; {new Date().getFullYear()} TheClashFruit</p>
</div>
</footer>
)
);
}

View file

@ -1,20 +0,0 @@
export default function Hero({ pageType, pageData }) {
if (pageType === 'page') {
return (
<div className={`h-[33vh] bg-slate-300 dark:bg-gray-900`}>
<div className={`max-w-5xl pb-4 lg:mx-auto max-lg:px-4 lg:px-0 h-[33vh] flex justify-start items-end`}>
<h1 className={`text-3xl font-serif font-medium`}>{ pageData.title }</h1>
</div>
</div>
)
} else if (pageType === 'post') {
return (
<div className={`h-[33vh] bg-slate-300 dark:bg-gray-900`}>
<div className={`max-w-5xl pb-4 lg:mx-auto max-lg:px-4 lg:px-0 h-[33vh] flex flex-col justify-end items-start`}>
<h1 className={`text-3xl font-serif font-medium mb-1`}>{ pageData.title }</h1>
<label>By { pageData.author }</label>
</div>
</div>
)
}
}

13
components/Input.js Normal file
View file

@ -0,0 +1,13 @@
import styles from '@/styles/Components.module.scss';
export default function Input({ className, type, label, ...props }) {
if (type === 'textarea') {
return (
<textarea className={className ? `${styles.inputText} ${className}` : styles.inputText} placeholder={label} {...props} />
);
}
return (
<input className={className ? `${styles.inputText} ${className}` : styles.inputText} type={type} placeholder={label} {...props} />
);
}

View file

@ -1,7 +0,0 @@
export default function Main({ children, className }) {
return (
<main className={`my-4 max-w-5xl lg:mx-auto max-lg:px-4 lg:px-0${className ? ' ' + className : ''}`}>
{ children }
</main>
)
}

35
components/Meta.js Normal file
View file

@ -0,0 +1,35 @@
import Head from 'next/head';
import { useRouter } from 'next/router';
export default function Meta({ pageData }) {
const router = useRouter();
return (
<Head>
<title>TheClashFruit &bull; {pageData.title}</title>
<link href="/favicon_light.ico" rel="icon" type="image/x-icon" media="(prefers-color-scheme: light)"/>
<link href="/favicon_dark.ico" rel="icon" type="image/x-icon" media="(prefers-color-scheme: dark)"/>
<meta property="tcf:page_data" content={JSON.stringify(pageData)} />
{pageData.type === 'page' && (
<>
<meta name="name" content={`TheClashFruit &bull; ${pageData.title}`}/>
<meta name="description" content="I'm TheClashFruit and I like to program, explore and craft stuff. I also like to play games. I have 3 Linux servers.. So, as you can see, I like to play around with Linux too. I'm currently learning how to write proper blog posts on my blog."/>
<meta name="keywords" content={`theclashfruit, tcf, blokkok, the, clash, fruit, ${pageData.title.trim().split(' ').join(', ').toLowerCase()}`}/>
<meta name="theme-color" content="#00796B"/>
<meta property="og:site_name" content="TheClashFruit" />
<meta property="og:title" content={pageData.title} />
<meta property="og:type" content="website" />
<meta property="og:locale" content="en_GB" />
<meta property="og:url" content="https://theclashfruit.me" />
<meta property="og:image" content="https://www.theclashfruit.me/img/logo.png" />
<meta property="og:description" content="I'm TheClashFruit and I like to program, explore and craft stuff. I also like to play games. I have 3 Linux servers.. So, as you can see, I like to play around with Linux too. I'm currently learning how to write proper blog posts on my blog."/>
</>
)}
</Head>
);
}

View file

@ -1,142 +1,107 @@
import Head from 'next/head';
import { useRouter } from 'next/router';
import { useRef } from 'react';
import Link from 'next/link';
export default function Navbar({ pageData }) {
const router = useRouter()
import Button from '@/components/Button';
import Dialog from '@/components/Dialog';
import Input from '@/components/Input';
const navRef = useRef();
import Logo from '@/public/icons/logo.svg';
import styles from '@/styles/Navbar.module.scss';
import {
Forward,
Mails,
Menu,
X
} from 'lucide-react';
import {
useEffect,
useRef,
useState
} from 'react';
export default function Navbar({ page }) {
const navRef = useRef();
const navCollapseRef = useRef();
const navButtonIconRef = useRef();
const navItemsRef = useRef();
const toggleNavBar = () => {
navRef.current.classList.toggle(`h-screen`);
if(navCollapseRef.current.classList.contains(`max-lg:hidden`)) {
navCollapseRef.current.classList.remove(`max-lg:hidden`);
} else {
navCollapseRef.current.classList.add(`max-lg:hidden`);
}
}
/*
const [ langData, setLangData ] = useState()
const [ loading, setLoading ] = useState(true)
const [ open, setOpen ] = useState(false);
const [ dialogOpen, setDialogOpen ] = useState(false);
useEffect(() => {
if(loading) {
fetch(`/locale/${router.locale}.json`).then(r => r.json()).then((locale) => {
setLangData(locale)
setLoading(false)
});
if(window.scrollY <= 30 && navRef.current !== null)
navRef.current.classList.remove(styles.navBarScrolled);
else if(navRef.current !== null)
navRef.current.classList.add(styles.navBarScrolled);
document.addEventListener('scroll', e => {
if(window.scrollY <= 1 && navRef.current !== null)
navRef.current.classList.remove(styles.navBarScrolled);
else if(navRef.current !== null)
navRef.current.classList.add(styles.navBarScrolled);
});
}, []);
const toggleNav = (e) => {
navRef.current.classList.toggle(styles.open);
if(navCollapseRef.current.classList.contains(styles.open)) {
navCollapseRef.current.classList.remove(styles.open);
setOpen(false);
} else {
navCollapseRef.current.classList.add(styles.open);
setOpen(true);
}
}, [ langData, setLangData, setLoading ])
if(loading) return (<></>)
console.log(langData)
<link type="application/json+oembed" href={`https://beta.theclashfruit.me/api/oembed?permalink=${pageData.postData.permalink}`} />
*/
};
return (
<>
<Head>
<title>TheClashFruit &bull; {pageData.title}</title>
<nav className={styles.navBar} ref={navRef}>
<div className={styles.container}>
<div className={styles.navLogoContainer}>
<Logo className={styles.navLogo} width={32} height={32} viewBox="0 0 24 24" />
<link href="/favicon_black.ico" rel="icon" media="(prefers-color-scheme: light)" />
<link href="/favicon_white.ico" rel="icon" media="(prefers-color-scheme: dark)" />
{pageData.type === 'page' && (
<>
<meta name="name" content={`TheClashFruit &bull; ${pageData.title}`} />
<meta name="description" content="I'm TheClashFruit and I like to program, explore and craft stuff. I also like to play games. I have 3 Linux servers.. So, as you can see, I like to play around with Linux too. I'm currently learning how to write proper blog posts on my blog." />
<meta name="keywords" content={`TheClashFruit, tcf, blokkok, the, clash, fruit, ${pageData.title.trim().split(' ').join(', ')}`} />
<meta name="theme-color" content="#00796B" />
<meta property="og:site_name" content="TheClashFruit" />
<meta property="og:title" content={pageData.title} />
<meta property="og:type" content="website" />
<meta property="og:locale" content={router.locale} />
<meta property="og:url" content="https://theclashfruit.me" />
<meta property="og:image" content="https://www.theclashfruit.me/img/logo.png" />
<meta property="og:description" content="I'm TheClashFruit and I like to program, explore and craft stuff. I also like to play games. I have 3 Linux servers.. So, as you can see, I like to play around with Linux too. I'm currently learning how to write proper blog posts on my blog." />
</>
)}
{pageData.type === 'post' && (
<>
<meta name="name" content={`TheClashFruit &bull; ${pageData.title}`} />
<meta name="description" content={pageData.postData.content.replace(/(<([^>]+)>)/gi, "").trim()} />
<meta name="keywords" content={`TheClashFruit, tcf, blokkok, the, clash, fruit, ${pageData.postData.title}`} />
<meta name="theme-color" content="#00796B" />
<meta property="og:site_name" content="TheClashFruit" />
<meta property="og:title" content={pageData.title} />
<meta property="og:type" content="article" />
<meta property="og:locale" content={router.locale} />
<meta property="og:url" content="https://theclashfruit.me" />
<meta property="og:image" content={pageData.postData.image} />
<meta property="og:description" content={pageData.postData.content.replace(/(<([^>]+)>)/gi, "").trim()} />
<meta property="article:published_time" content={new Date(pageData.postData.created * 1000).toISOString()} />
<meta property="article:modified_time" content={new Date(pageData.postData.updated * 1000).toISOString()} />
<meta property="article:author" content={pageData.postData.author} />
<meta property="article:section" content={`Technology`} />
<meta name="twitter:card" content="summary_large_image" />
<meta property="twitter:domain" content="theclashfruit.me" />
<meta property="twitter:url" content={`https://theclashfruit.me/post/${pageData.postData.permalink}`} />
<meta name="twitter:title" content={pageData.postData.title} />
<meta name="twitter:description" content={pageData.postData.content.replace(/(<([^>]+)>)/gi, "").trim()} />
<meta name="twitter:image" content={pageData.postData.image} />
</>
)}
<script async src="https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-1510964912637528" crossorigin="anonymous"></script>
</Head>
<nav className={`transition z-10 bg-slate-300/70 dark:bg-gray-900/70 backdrop-blur-sm fixed top-0 left-0 right-0`} ref={navRef}>
<div className={`flex items-center lg:justify-between max-lg:flex-col flex-row max-w-5xl lg:mx-auto max-lg:px-3 max-lg:py-3 lg:px-0 h-full`}>
<div className={`flex max-lg:w-full justify-between items-center`}>
<div>
<svg xmlns="http://www.w3.org/2000/svg" height="28" viewBox="0 -960 960 960" width="28" className={`fill-teal-700`}>
<path d="M202.565-165.326q-46.229-46.48-71.973-108.878-25.744-62.397-25.744-126.274 0-75.236 27.178-139.044 27.179-63.809 79.061-114.913 39.435-40.435 98.728-65.674 59.294-25.239 134.75-38.119 75.457-12.881 165.837-15.022 90.381-2.141 191.484 4.701 8.744 100.527 7.864 190.288-.88 89.761-13.402 166.087-12.522 76.326-38.381 137.433-25.858 61.106-67.293 102.415-51.985 52.23-114.027 79.354-62.042 27.124-130.955 27.124-72.929 0-132.289-23.5-59.36-23.5-110.838-75.978Zm114.631-12.587q25.592 17 58.964 25.761t69.284 8.761q51.624 0 101.612-21.881 49.987-21.88 89.748-62.402 29.479-30.956 49.218-80.272 19.739-49.315 29.761-110.674 10.021-61.358 12.663-132.304 2.641-70.946-.359-145.163-92.522-2.239-166.544 2.261-74.021 4.5-131.043 16.5-57.022 12-97.163 31.12-40.141 19.119-65.101 44.641-41.845 42.043-63.964 89.466-22.12 47.422-22.12 97.058 0 46.154 19.504 97.676 19.505 51.523 49.609 83.213 50.213-95.805 123.496-172.087 73.282-76.283 161-124-85.283 76-139.424 163.141-54.141 87.141-79.141 189.185Zm0 0Zm0 0Z"/>
</svg>
<span className="sr-only">TheClashFruit's logo, a leaf.</span>
</div>
<button className={`lg:hidden`} onClick={toggleNavBar}>
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24" className={`fill-black dark:fill-white`} ref={navButtonIconRef}>
<path d="M141.957-224.782q-16.946 0-28.049-11.458-11.104-11.457-11.104-27.793 0-16.337 11.104-27.315 11.103-10.978 28.049-10.978h677.086q15.971 0 27.181 11.154 11.211 11.153 11.211 27.489t-11.211 27.618q-11.21 11.283-27.181 11.283H141.957Zm0-216.827q-16.946 0-28.049-11.457-11.104-11.458-11.104-27.555 0-16.336 11.104-27.314 11.103-10.978 28.049-10.978h677.086q15.971 0 27.181 11.154 11.211 11.153 11.211 27.489 0 16.097-11.211 27.379-11.21 11.282-27.181 11.282H141.957Zm0-216.826q-16.946 0-28.049-11.338-11.104-11.338-11.104-27.913 0-16.097 11.104-27.195 11.103-11.097 28.049-11.097h677.086q15.971 0 27.181 11.273 11.211 11.273 11.211 27.37 0 16.575-11.211 27.738-11.21 11.162-27.181 11.162H141.957Z"/>
</svg>
<span className="sr-only">Open Menu</span>
</button>
<Button className={styles.navToggle} icon={open ? X : Menu} type="icon" onClick={toggleNav} />
</div>
<div className={`max-lg:hidden max-lg:h-full`} ref={navCollapseRef}>
<ul className={`flex max-lg:flex-1 max-lg:p-4 max-lg:w-screen max-lg:h-full max-lg:items-start max-lg:justify-center max-lg:flex-col`}>
<li className={`p-3`}>
<Link href={pageData.active === 'home' ? "#" : "/"} className={`max-lg:text-lg text-black dark:text-white ${pageData.active !== 'home' ? 'text-opacity-60 dark:text-opacity-60' : ''} hover:text-opacity-100 rounded-sm focus:outline outline-teal-700/70 outline-offset-4 outline-2`}>Home</Link>
<div className={styles.navCollapse} ref={navCollapseRef}>
<ul className={styles.navLinks}>
<li>
<Link href={page === 'home' ? '#' : '/'} className={page === 'home' ? `${styles.active}` : ''}>Home</Link>
</li>
<li className={`p-3`}>
<Link href={pageData.active === 'blog' ? "#" : "/blog"} className={`max-lg:text-lg transition text-black ${pageData.active !== 'blog' ? 'text-opacity-60 dark:text-opacity-60' : ''} dark:text-white hover:text-opacity-100 rounded-sm focus:outline outline-teal-700/70 outline-offset-4 outline-2`}>Blog</Link>
<li>
<Link href={page === 'projects' ? '#' : '/projects'} className={page === 'projects' ? styles.active : ''}>Projects</Link>
</li>
<li className={`p-3`}>
<Link href={pageData.active === 'projects' ? "#" : "/projects"} className={`max-lg:text-lg transition text-black ${pageData.active !== 'projects' ? 'text-opacity-60 dark:text-opacity-60' : ''} dark:text-white hover:text-opacity-100 rounded-sm focus:outline outline-teal-700/70 outline-offset-4 outline-2`}>Projects</Link>
<li>
<Link href={page === 'gallery' ? '#' : '/gallery'} className={page === 'gallery' ? styles.active : ''}>Gallery</Link>
</li>
<li className={`p-3`}>
<Link href={pageData.active === 'gallery' ? "#" : "/gallery"} className={`max-lg:text-lg transition text-black ${pageData.active !== 'gallery' ? 'text-opacity-60 dark:text-opacity-60' : ''} dark:text-white hover:text-opacity-100 rounded-sm focus:outline outline-teal-700/70 outline-offset-4 outline-2`}>Gallery</Link>
<li>
<Link href={page === 'blog' ? '#' : '/blog'} className={page === 'blog' ? styles.active : ''}>Blog</Link>
</li>
<li className={`p-3`}>
<Link href={pageData.active === 'contact' ? "#" : "/contact"} className={`max-lg:text-lg transition text-black ${pageData.active !== 'contact' ? 'text-opacity-60 dark:text-opacity-60' : ''} dark:text-white hover:text-opacity-100 rounded-sm focus:outline outline-teal-700/70 outline-offset-4 outline-2`}>Contact</Link>
</ul>
<ul>
<li>
<Button icon={Mails} type="primary" onClick={() => { setDialogOpen(true); }}>Contact</Button>
</li>
</ul>
</div>
</div>
</nav>
{ dialogOpen &&
<Dialog title="Contact" closeAction={() => { setDialogOpen(false); }}>
<form>
<Input required name="name" type="text" label="Name" />
<Input required name="email" type="email" label="E-Mail" />
<Input required name="msg" type="textarea" label="Message" />
<Button icon={Forward} type="primary">Send</Button>
</form>
</Dialog>
}
</>
)
);
}

View file

@ -0,0 +1,9 @@
import styles from '@/styles/Components.module.scss';
export default function ProgressBar({ className, progress, ...props }) {
return (
<div className={className ? `${styles.progressBar} ${className}` : styles.progressBar} {...props}>
<div className={styles.progressBarProgress} style={{ width: `${progress}%` }}></div>
</div>
);
}

View file

@ -1,118 +0,0 @@
import { useEffect, useRef } from 'react';
import { getSettings, setSettings } from '@/lib/SettingsLib';
export default function SettingsOverlay() {
const settingsPanelRef = useRef();
const settingsButtonRef = useRef();
const settingsBgRef = useRef();
const settingOptionThemeRef = useRef();
const settingOptionLangRef = useRef();
const settingOptionBlurRef = useRef();
const settingOptionTrackingRef = useRef();
useEffect(() => {
window.addEventListener('scroll', () => {
if(window.scrollY > 50) {
settingsButtonRef.current.classList.remove('scale-100');
settingsButtonRef.current.classList.add('scale-0');
setTimeout(() => {
settingsButtonRef.current.classList.add('hidden');
settingsButtonRef.current.classList.remove('flex');
}, 100);
} else {
settingsButtonRef.current.classList.remove('hidden');
settingsButtonRef.current.classList.add('flex');
setTimeout(() => {
settingsButtonRef.current.classList.remove('scale-0');
settingsButtonRef.current.classList.add('scale-100');
}, 100);
}
});
}, []);
useEffect(() => {
if(!localStorage.getItem("tcf_settings"))
localStorage.setItem("tcf_settings", JSON.stringify({ v: 1, theme: window.matchMedia("(prefers-color-scheme: dark)") ? 'dark' : 'light', language: 'en', blur: true, tracking: true, ads: true }))
settingOptionThemeRef.current.value = getSettings().theme;
settingOptionBlurRef.current.checked = getSettings().blur;
settingOptionTrackingRef.current.checked = getSettings().tracking;
}, [ setSettings, getSettings ]);
const onSettingsButtonClick = () => {
settingsPanelRef.current.classList.toggle('hidden');
settingsPanelRef.current.classList.toggle('flex');
settingsBgRef.current.classList.toggle('hidden');
settingsBgRef.current.classList.toggle('block');
}
return (
<>
<div className={`bg-slate-300 dark:bg-gray-900 fixed bottom-20 left-4 p-3 rounded-md shadow-lg hidden flex-col z-30`} ref={settingsPanelRef}>
<h1 className={`text-xl font-serif font-medium mb-2`}>Settings</h1>
<div className={`flex justify-between items-center mb-2`}>
<div className={`flex flex-col mr-4`}>
<label className={`font-bold`}>Theme</label>
<label>Choose your preferred theme.</label>
</div>
<select className={`rounded-md border-gray-950/50 bg-gray-950/40 accent-teal-700 cursor-pointer`} onChange={(e) => { setSettings('theme', e.target.value) }} ref={settingOptionThemeRef}>
<option value="dark">Dark</option>
<option value="light">Light</option>
</select>
</div>
<div className={`hidden justify-between items-center mb-2`}>
<div className={`flex flex-col mr-4`}>
<label className={`font-bold`}>Language</label>
<label>Choose your preferred language.</label>
</div>
<select className={`rounded-md border-gray-950/50 bg-gray-950/40 accent-teal-700 cursor-pointer`}>
<option>🇬🇧 English (English, UK)</option>
<option>🇭🇺 Magyar (Hungarian)</option>
<option>🇫🇷 Français (French)</option>
<option>🇩🇪 Deutsch (German)</option>
</select>
</div>
<div className={`flex justify-between items-center mb-2`}>
<div className={`flex flex-col mr-4`}>
<label className={`font-bold`}>Blur Effects</label>
<label>Disable it if it hurts performance.</label>
</div>
<input type="checkbox" className={`rounded-md border-gray-950/50 bg-gray-950/40 accent-teal-700 cursor-pointer`} onChange={(e) => { setSettings('blur', e.target.checked) }} ref={settingOptionBlurRef} />
</div>
<div className={`flex justify-between items-center mb-2`}>
<div className={`flex flex-col mr-4`}>
<label className={`font-bold`}>Analytics</label>
<label>Disable it if you want to opt out from analytics.</label>
</div>
<input type="checkbox" className={`rounded-md border-gray-950/50 bg-gray-950/40 accent-teal-700 cursor-pointer`} onChange={(e) => { setSettings('tracking', e.target.checked) }} ref={settingOptionTrackingRef} />
</div>
<div className={`hidden`}>
<div className={`flex flex-col mr-4`}>
<label className={`font-bold`}>Advertisements</label>
<label>You can also consider supporting me by donating.</label>
</div>
<input type="checkbox" className={`rounded-md border-gray-950/50 bg-gray-950/40 accent-teal-700 cursor-pointer`} onChange={(e) => { setSettings('ads', e.target.checked) }} />
</div>
</div>
<div className={`fixed hidden bottom-0 left-0 right-0 top-0 bg-slate-300/70 dark:bg-gray-900/70 backdrop-blur-sm z-20`} onClick={onSettingsButtonClick} ref={settingsBgRef} />
<button className={`transition-all bg-slate-300 hover:bg-teal-700 focus:bg-teal-700 dark:bg-gray-900 focus:outline outline-teal-700/70 outline-offset-2 outline-2 p-3 rounded-full shadow-lg fixed bottom-4 left-4 z-30`} ref={settingsButtonRef} onClick={onSettingsButtonClick}>
<svg xmlns="http://www.w3.org/2000/svg" height="24" viewBox="0 -960 960 960" width="24" className={`fill-black dark:fill-white`}>
<path d="M551.761-62.804H408.239q-14.736 0-26.064-8.479-11.327-8.478-12.327-23.674l-15.761-98.565q-15.522-5.521-34.24-16.031-18.718-10.51-32.369-21.534l-90.565 41.761q-12.717 7.239-27.61 1.762-14.892-5.477-22.289-19.042L75.283-335.804q-8.718-12.904-4.479-26.998 4.24-14.094 15.718-22.111l82.282-61.064q-1-6.666-1.38-17.182-.381-10.515-.381-18.482 0-7.099.381-16.688.38-9.59 1.38-17.758l-82.282-59.761q-12.24-8.717-15.718-22.876-3.478-14.16 4.24-27.233l71.999-128.521q8.479-12.718 22.435-17.837 13.957-5.12 27.435 1.119l89.522 41.522q13.016-10.152 32.312-21.239 19.296-11.087 35.34-16.609l15.761-98.521q1-14.957 12.418-23.555 11.418-8.598 25.973-8.598h143.522q14.68 0 26.189 8.67 11.51 8.669 13.202 23.483l14.761 98.282q16.522 5.522 36.065 16.663 19.544 11.141 32.065 21.424l89.044-41.761q12.717-6.239 27.61-.952 14.892 5.287 22.307 18.006l72.144 127.327q7.765 12.938 4.287 27.713-3.478 14.776-14.957 23.493l-84.282 58.283q1 8.654 2.119 19.282 1.12 10.628 1.12 18.283 0 7.655-1.12 17.783-1.119 10.128-1.358 17.782l82.76 59.283q11.479 9.102 15.218 23.246 3.739 14.145-3.766 27.176l-72.186 129.134q-8.505 14.031-22.842 18.77-14.337 4.739-27.054-2.5l-90.565-41.761q-13.283 10.522-31.924 21.783-18.641 11.261-33.685 15.782l-15.761 98.565q-2 15.196-13.418 23.674-11.418 8.479-25.973 8.479ZM479-340.826q58.116 0 99.025-40.868 40.91-40.868 40.91-98.925 0-57.829-40.91-98.953-40.909-41.124-99.025-41.124-58.565 0-99.25 40.983-40.685 40.982-40.685 98.811 0 58.057 40.685 99.066 40.685 41.01 99.25 41.01ZM478.382-410Q449-410 428.62-430.826q-20.381-20.826-20.381-50.053 0-29.226 20.586-49.934 20.587-20.709 49.881-20.709 29.055 0 50.055 20.826t21 50.053q0 29.226-21.118 49.935Q507.526-410 478.382-410Zm1.857-70.761Zm-42.087 340.652h83.944l14.241-112.521q33.989-8.24 64.008-25.185 30.018-16.945 55.163-42.294l105.057 46.239 37.848-68.891-93-68.522q4.239-16.875 7.239-34.265 3-17.391 3-35.302t-2.619-34.77q-2.62-16.858-6.62-34.38l92.239-68-37.326-68.891-105.956 45.478q-23.044-26.478-53.237-45.012t-66.448-23.466l-12.837-110.761h-85.696l-13 110.761q-35.761 7.761-66.5 25.38-30.739 17.62-53.978 42.859l-103.717-45.239L160.826-618l92.761 68.283q-4.239 17.76-6.739 34.619-2.5 16.859-2.5 34.076 0 17.218 2.5 34.457t6.739 35.282l-92.761 68.522 38.848 68.891 104.761-46.239q25.761 26.239 56 43.479 30.239 17.239 63.717 25l14 111.521Z"/>
</svg>
<span className="sr-only">Open Site Settings</span>
</button>
</>
)
}

View file

@ -1,6 +0,0 @@
import mongoose from 'mongoose';
mongoose.connect(process.env.DATABASE_CONN)
.then(r => console.log('Connected to MongoDB!'))
.catch(e => console.error(e))

View file

@ -1,21 +0,0 @@
function getSettings() {
if (typeof window === 'undefined')
return;
return JSON.parse(
localStorage.getItem("tcf_settings") || '{ "v": 1, "theme": "dark", "language": "en", "blur": true, "tracking": true, "ads": true }'
)
}
function setSettings(setting, value) {
const oldSettings = getSettings();
oldSettings[setting] = value;
localStorage.setItem("tcf_settings", JSON.stringify(oldSettings))
}
module.exports = {
getSettings,
setSettings
}

View file

View file

View file

@ -4,11 +4,7 @@ const path = require('path');
const nextConfig = {
reactStrictMode: true,
sassOptions: {
includePaths: [ path.join(__dirname, 'styles') ],
},
experimental: {
esmExternals: "loose",
serverComponentsExternalPackages: ["mongoose"]
includePaths: [ path.join(__dirname, 'styles') ]
},
generateBuildId: async () => {
const commitFetch = await fetch('https://git.theclashfruit.me/api/v1/repos/TheClashFruit/Website/commits?sha=main&limit=1')
@ -16,49 +12,27 @@ const nextConfig = {
return commitData[0].sha
},
redirects: async () => {
return [
{
source: '/post/:year/:permalink',
destination: '/post/:permalink',
permanent: true,
},
]
},
headers: async () => {
return [
{
source: '/:path*',
headers: [
{
key: 'Strict-Transport-Security',
value: 'max-age=5184000; includeSubDomains'
}
]
},
{
source: '/api/:path*',
headers: [
{
key: 'Strict-Transport-Security',
value: 'max-age=5184000; includeSubDomains'
},
{
key: 'Access-Control-Allow-Origin',
value: '*'
}
]
}
]
];
},
webpack(config) {
config.module.rules.push({
test: /\.svg$/,
use: [ '@svgr/webpack' ]
});
return config;
}
}
};
module.exports = nextConfig
/*
i18n: {
locales: ['en', 'hu', 'fr', 'de'],
defaultLocale: 'en'
},
*/
module.exports = nextConfig;

3494
package-lock.json generated

File diff suppressed because it is too large Load diff

View file

@ -1,5 +1,5 @@
{
"name": "newdesignedsite",
"name": "site",
"version": "0.1.0",
"private": true,
"scripts": {
@ -9,23 +9,17 @@
"lint": "next lint"
},
"dependencies": {
"@socialgouv/matomo-next": "^1.6.1",
"autoprefixer": "10.4.14",
"mongoose": "^7.4.0",
"next": "13.4.10",
"postcss": "8.4.26",
"react": "18.2.0",
"react-dom": "18.2.0",
"showdown": "^2.1.0",
"showdown-footnotes": "^2.1.2",
"showdown-highlight": "^3.1.0",
"showdown-youtube": "^1.2.1",
"tailwindcss": "3.3.3"
"@icons-pack/react-simple-icons": "^9.3.0",
"@socialgouv/matomo-next": "^1.8.0",
"@svgr/webpack": "^8.1.0",
"lucide-react": "^0.312.0",
"next": "14.0.4",
"react": "^18",
"react-dom": "^18"
},
"devDependencies": {
"@tailwindcss/forms": "^0.5.4",
"@tailwindcss/typography": "^0.5.9",
"sass": "^1.64.0",
"tailwind-scrollbar": "^3.0.4"
"eslint": "^8",
"eslint-config-next": "14.0.4",
"sass": "^1.70.0"
}
}

View file

@ -1,48 +1,11 @@
import Navbar from '@/components/Navbar';
import Link from 'next/link';
import Hero from '@/components/Hero';
import Main from '@/components/Main';
import Footer from '@/components/Footer';
export default function NotFound() {
const textPool = [
"My bad you should have not ended up here.",
"What are you looking for mate?",
"You're very suspicious to me, why are you here?",
"edef08f710f72a96c3a720f97082a12e7e690892cead11db3ffb0d2a415cdba33bbbbc33402f1f8ca14a8adff43e14cc0fc374a8b7aa2dabcda20638f5721847"
]
const textPoolUwU = [
"My bad u shouwd have not ended up hewe. UwU",
"Wat awe u wooking fow mate? 'w'",
"U'we vewy suspicious to me, why awe u hewe? OwO",
"3d7336510a6cb83a52f0ef3c587819ee38e7890b5a841e5b8b8486e486030cc9876a611498fc1a198ed9b35a143199514429193a16c365a7e4542603315906e4"
]
const randomText =
[1, 0, 0, 0, 0].sort(() => Math.random() - 0.5)[0] ?
textPoolUwU[Math.floor(Math.random() * textPoolUwU.length)] :
textPool[Math.floor(Math.random() * textPool.length)];
import Meta from '@/components/Meta';
export default function Home() {
return (
<>
<Navbar pageData={{ title: '404 Not Found', active: '404', type: 'page' }} />
<Hero pageType="page" pageData={{ title: '404' }} />
<Main className={`prose dark:prose-invert`}>
<p>This page is non existent. 🗿</p>
<h4>You might be interested in:</h4>
<ul>
<li>The <Link href="/">home</Link>page.</li>
<li>My <Link href="/blog">blog</Link>.</li>
<li>My <Link href="/projects">project</Link>s.</li>
</ul>
<h4>Just a message for you:</h4>
<pre suppressHydrationWarning={true}>{randomText}</pre>
</Main>
<Footer />
<Meta pageData={{ title: '404', type: 'page' }} />
<Navbar page={'404'} />
</>
)
}
);
}

View file

@ -1,20 +1,37 @@
import '@/styles/globals.scss'
import { useEffect } from 'react';
import init from '@socialgouv/matomo-next';
import SettingsOverlay from '@/components/SettingsOverlay';
import '@/styles/globals.scss';
import ConsentBanner from '@/components/ConsentBanner';
import { init, push } from '@socialgouv/matomo-next';
import {useEffect, useState} from 'react';
import Meta from '@/components/Meta';
export default function App({ Component, pageProps }) {
const [ isDismissed, setIsDismissed ] = useState(true);
useEffect(() => {
setIsDismissed(localStorage.getItem('tcf_consent') !== null);
window.addEventListener('consentChange', () => {
setIsDismissed(localStorage.getItem('tcf_consent') !== null);
});
init({
url: 'https://matomo.theclashfruit.me',
siteId: 1
});
}, []);
const isAccepted = localStorage.getItem('tcf_consent') === 'true';
if(isAccepted)
push(['setCookieConsentGiven']);
else
push(['requireCookieConsent']);
}, [ setIsDismissed ]);
return (
<>
{ !isDismissed && <ConsentBanner /> }
<Component {...pageProps} />
<SettingsOverlay />
</>
)
);
}

View file

@ -1,13 +1,14 @@
import { Html, Head, Main, NextScript } from 'next/document'
import { Html, Head, Main, NextScript } from 'next/document';
import ConsentBanner from '@/components/ConsentBanner';
export default function Document() {
return (
<Html lang="en">
<Head />
<body className={`text-black bg-slate-200 dark:text-white dark:bg-slate-800 scrollbar-thin scrollbar-track-slate-200 scrollbar-thumb-teal-700/80 dark:scrollbar-track-slate-800`}>
<body>
<Main />
<NextScript />
</body>
</Html>
)
);
}

View file

@ -1,18 +0,0 @@
export default function Login() {
return (
<div className={`flex flex-col gap-12 items-center justify-center h-[100svh] w-[100svw]`}>
<h1 className={`text-3xl font-serif font-medium`}>Login</h1>
<form className={`flex flex-col gap-2 w-1/4`}>
<input type="email" name="email" placeholder="E-Mail" className={`transition rounded-md border border-slate-400 bg-slate-300 dark:border-gray-950 dark:bg-gray-900 accent-teal-700`} />
<input type="password" name="password" placeholder="Password" className={`transition rounded-md border border-slate-400 bg-slate-300 dark:border-gray-950 dark:bg-gray-900 accent-teal-700`} />
<button className={`transition rounded-md border border-slate-400 bg-slate-300 dark:border-gray-950 dark:bg-gray-900 accent-teal-700 py-2 px-3`}>Continue</button>
</form>
<form className={`hidden`}>
<input type="text" placeholder="OTP" className={`transition rounded-md border border-slate-400 bg-slate-300 dark:border-gray-950 dark:bg-gray-900 accent-teal-700`} />
<button className={`transition rounded-md border border-slate-400 bg-slate-300 dark:border-gray-950 dark:bg-gray-900 accent-teal-700 py-2 px-3`}>Login</button>
</form>
</div>
)
}

View file

@ -1,23 +0,0 @@
import fs from 'fs';
export default async function handler(req, res) {
const fileBuffer = fs.readFileSync(".next/BUILD_ID")
const BUILD_ID = fileBuffer.toString();
const commitFetch = await fetch(`https://git.theclashfruit.me/api/v1/repos/TheClashFruit/Website/commits?sha=${BUILD_ID}&limit=1`)
const commitData = await commitFetch.json()
console.log(`https://git.theclashfruit.me/api/v1/repos/TheClashFruit/Website/commits?sha=${BUILD_ID}&limit=1`, '>', commitData[0])
res.status(200).json({
buildId: BUILD_ID,
git: {
repo: 'https://git.theclashfruit.me/TheClashFruit/Website',
commit: `https://git.theclashfruit.me/TheClashFruit/Website/commit/${BUILD_ID}`,
message: commitData[0].commit.message,
files: commitData[0].files,
stats: commitData[0].stats,
}
})
}

13
pages/api/index.js Normal file
View file

@ -0,0 +1,13 @@
// Next.js API route support: https://nextjs.org/docs/api-routes/introduction
export default function handler(req, res) {
res.status(200).json({
latestVersion: 0,
versions: [
{
name: '1.0.0',
swagger: 'swagger.json',
}
]
});
}

View file

@ -1,20 +0,0 @@
export default async function handler(req, res) {
const { permalink } = req.query
const postFetch = await fetch(`https://theclashfruit.me/api/v1/post/${permalink}`)
if(postFetch.status === 200) {
const postData = await postFetch.json()
res.status(200).json({
type: 'link',
version: '1.0',
title: postData.title,
author_name: postData.author,
author_url: 'https://theclashfruit.me',
thumbnail_url: postData.image,
})
} else {
res.status(postFetch.status).json({});
}
}

38
pages/api/v1/gallery.js Normal file
View file

@ -0,0 +1,38 @@
export default function handler(req, res) {
res.status(200).json([
{
alt: 'A black & white cat.',
preview: 'https://cdn-new.theclashfruit.me/gallery/IMG_1799.jpg',
image: {
cr3: {
name: 'IMG_1799.CR3',
url: 'https://cdn-new.theclashfruit.me/gallery/cr3/IMG_1799.CR3'
},
jpg: {
name: 'IMG_1799.jpg',
url: 'https://cdn-new.theclashfruit.me/gallery/IMG_1799.jpg'
}
}
},
{
alt: 'Dandelions in the sunset.',
preview: 'https://cdn-new.theclashfruit.me/gallery/IMG_1707.jpg',
image: {
jpg: {
name: 'IMG_1707.jpg',
url: 'https://cdn-new.theclashfruit.me/gallery/IMG_1707.jpg'
}
}
},
{
alt: 'Budapest from the MOL building.',
preview: 'https://cdn-new.theclashfruit.me/gallery/20231109_122641.jpg',
image: {
jpg: {
name: '20231109_122641.jpg',
url: 'https://cdn-new.theclashfruit.me/gallery/20231109_122641.jpg'
}
}
}
]);
}

View file

@ -1,64 +0,0 @@
import Navbar from '@/components/Navbar';
import BlogItem from '@/components/BlogItem';
import showdown from 'showdown';
import Hero from '@/components/Hero';
import Footer from '@/components/Footer';
import AdBanner from '@/components/AdBanner';
export default function Blog({ blogData }) {
return (
<>
<Navbar pageData={{ title: 'Blog', active: 'blog', type: 'page' }} />
<Hero pageType="page" pageData={{ title: 'Blog' }} />
<main className={`grid grid-cols-3 my-4 max-md:grid-cols-1 max-lg:grid-cols-2 auto-rows-max grid-flow-row gap-4 max-w-5xl lg:mx-auto max-lg:mx-4 lg:px-0`}>
{blogData.map((blog, i) => (
blog.ad ? (
<AdBanner
data-ad-slot="5301256152"
data-ad-layout-key="-62+di+47-55+1l"
data-ad-format="fluid"
/>
) : (
<BlogItem
blogData={blog}
key={i}
/>
)
))}
</main>
<Footer />
</>
)
}
export async function getServerSideProps() {
const blogFetch = await fetch('https://theclashfruit.me/api/v1/posts')
const blogData = await blogFetch.json()
const converter = new showdown.Converter();
converter.setFlavor('github');
const finalBlogData = blogData.data.map((blog) => {
blog.content = converter.makeHtml(blog.content);
blog.ad = false;
return blog;
});
const reverse = finalBlogData.reverse();
reverse.splice(1, 0, { ad: true });
reverse.splice(7, 0, { ad: true });
return {
props: {
blogData: reverse,
}
}
}

View file

@ -1,20 +0,0 @@
import Navbar from '@/components/Navbar';
import Hero from '@/components/Hero';
import Main from '@/components/Main';
import Footer from '@/components/Footer';
export default function Contact() {
return (
<>
<Navbar pageData={{ title: 'Contact', active: 'contact', type: 'page' }} />
<Hero pageType="page" pageData={{ title: 'Contact' }} />
<Main>
<p>tba</p>
</Main>
<Footer />
</>
)
}

View file

@ -1,20 +0,0 @@
import Navbar from '@/components/Navbar';
import Hero from '@/components/Hero';
import Main from '@/components/Main';
import Footer from '@/components/Footer';
export default function Gallery() {
return (
<>
<Navbar pageData={{ title: 'Gallery', active: 'gallery', type: 'page' }} />
<Hero pageType="page" pageData={{ title: 'Gallery' }} />
<Main>
<p>tba</p>
</Main>
<Footer />
</>
)
}

View file

@ -1,24 +1,482 @@
import Navbar from '@/components/Navbar';
import Hero from '@/components/Hero';
import Main from '@/components/Main';
import Meta from '@/components/Meta';
import Button from '@/components/Button';
import Card from '@/components/Card';
import ProgressBar from '@/components/ProgressBar';
import Footer from '@/components/Footer';
import Dialog from '@/components/Dialog';
import Input from '@/components/Input';
import {
SiYoutube,
SiForgejo,
SiModrinth,
SiGithub,
SiDiscord,
SiMastodon,
SiCurseforge,
SiJavascript,
SiKotlin,
SiRust,
SiHtml5,
SiCss3,
SiCsharp,
SiPython,
SiFigma,
SiDavinciresolve
} from '@icons-pack/react-simple-icons';
import {
ArrowDown,
Forward
} from 'lucide-react';
import styles from '@/styles/Home.module.scss';
export default function Home() {
// Hi! I'm TheClashFruit, I like to make websites, discord bots and more related to programming.
let skills = [
{
key: 'html',
icon: SiHtml5,
name: 'HTML',
percent: 100
},
{
key: 'css',
icon: SiCss3,
name: 'CSS',
percent: 98
},
{
key: 'js',
icon: SiJavascript,
name: 'JavaScript',
percent: 99
},
{
key: 'kt',
icon: SiKotlin,
name: 'Kotlin',
percent: 77
},
{
key: 'rs',
icon: SiRust,
name: 'Rust',
percent: 34
},
{
key: 'cs',
icon: SiCsharp,
name: 'C#',
percent: 78
},
{
key: 'py',
icon: SiPython,
name: 'Python',
percent: 69
},
{
key: 'fg',
icon: SiFigma,
name: 'Figma',
percent: 76
},
{
key: 'dr',
icon: SiDavinciresolve,
name: 'DaVinci Resolve',
percent: 12
}
];
skills.sort((a, b) => b.percent - a.percent);
return (
<>
<Navbar pageData={{ title: 'Home', active: 'home', type: 'page' }} />
<Meta pageData={{ title: 'Home', type: 'page' }} />
<Navbar page="home" />
<Hero pageType="page" pageData={{ title: 'Home' }} />
<header className={styles.hero}>
<div className={styles.container}>
<div className={styles.heroTop}>
<h1>TheClashFruit</h1>
<p>A full-stack web, mobile developer & mod creator.</p>
<Main>
<p>Hi! I'm TheClashFruit, I like to make websites, discord bots and more related to programming.</p>
<ul className={styles.socialIcons}>
<li>
<Button href="https://youtube.com/@TheClashFruit" target="_blank" rel="noopener noreferrer me" icon={SiYoutube} type="icon" title="YouTube"/>
</li>
<li>
<Button href="https://discord.gg/CWEApqJ6rc" target="_blank" rel="noopener noreferrer me" icon={SiDiscord} type="icon" title="Discord"/>
</li>
<li>
<Button href="https://mas.to/@TheClashFruit" target="_blank" rel="noopener noreferrer me" icon={SiMastodon} type="icon" title="Mastodon"/>
</li>
<li>
<Button href="https://modrinth.com/user/TheClashFruit" target="_blank" rel="noopener noreferrer me" icon={SiModrinth} type="icon" title="Modrinth"/>
</li>
<li>
<Button href="https://www.curseforge.com/members/theclashfruit" target="_blank" rel="noopener noreferrer me" icon={SiCurseforge} type="icon" title="CurseForge"/>
</li>
<li>
<Button href="https://git.theclashfruit.me/TheClashFruit" target="_blank" rel="noopener noreferrer me" icon={SiForgejo} type="icon" title="Forgejo"/>
</li>
<li>
<Button href="https://github.com/TheClashFruit" target="_blank" rel="noopener noreferrer me" icon={SiGithub} type="icon" title="GitHub" />
</li>
</ul>
</div>
<h2 className={`text-2xl font-serif font-medium my-4`}>Skills</h2>
</Main>
<Button href="#skills" icon={ArrowDown} type="icon" title="Scroll Down" />
</div>
</header>
<Footer />
<main>
<div className={styles.container}>
<div id="skills">
<h2>Skills</h2>
<div className={styles.skillsGrid}>
{skills.map(skill => (
<Card className={styles.skillCard} key={skill.key}>
<skill.icon size={48}/>
<div className={styles.skillInner}>
<div className={styles.skillLabel}>
<h3>{skill.name}</h3>
<label>{skill.percent}%</label>
</div>
<ProgressBar progress={skill.percent}/>
</div>
</Card>
))}
</div>
</div>
<div id="lorem">
<h2>Lorem Ipsum</h2>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
<p>
Lorem ipsum dolor sit amet, consectetur adipisicing elit. Ad amet corporis culpa cupiditate deleniti
deserunt eligendi iusto modi nulla, obcaecati odit officia provident repudiandae sed sequi sit suscipit,
ullam velit.
</p>
</div>
</div>
</main>
<Footer/>
</>
)
);
}

View file

@ -1,68 +0,0 @@
import Navbar from '@/components/Navbar';
import showdown from 'showdown';
import showdownHighlight from 'showdown-highlight'
import Hero from '@/components/Hero';
import 'showdown-youtube';
import footnotes from 'showdown-footnotes';
import Footer from '@/components/Footer';
import Link from 'next/link';
import AdBanner from '@/components/AdBanner';
export default function Post({ postData }) {
return (
<>
<Navbar pageData={{ title: postData.title, active: 'post', type: 'post', postData }} />
<Hero pageType="post" pageData={{ title: postData.title, author: postData.author }} />
<article className={`prose my-4 dark:prose-invert max-w-5xl lg:mx-auto max-lg:px-4 lg:px-0`} dangerouslySetInnerHTML={{ __html: postData.content }} />
<div className="max-w-5xl pb-4 lg:mx-auto max-lg:px-4 lg:px-0">
<AdBanner
data-ad-slot="6363427510"
data-ad-format="auto"
data-full-width-responsive="true"
/>
</div>
<Footer />
</>
)
}
export async function getServerSideProps(context) {
const postFetch = await fetch(`https://theclashfruit.me/api/v1/post/${context.params.permalink}`)
if(postFetch.status !== 200) {
return {
notFound: true
}
}
const postData = await postFetch.json()
const converter = new showdown.Converter({
extensions: [
showdownHighlight({
pre: true,
auto_detection: true
}),
'youtube',
footnotes
]
});
converter.setFlavor('github');
console.log(converter.getOptions().extensions)
postData.content = converter.makeHtml(postData.content);
return {
props: {
postData,
}
}
}

View file

@ -1,20 +0,0 @@
import Navbar from '@/components/Navbar';
import Hero from '@/components/Hero';
import Main from '@/components/Main';
import Footer from '@/components/Footer';
export default function Projects() {
return (
<>
<Navbar pageData={{ title: 'Projects', active: 'projects', type: 'page' }} />
<Hero pageType="page" pageData={{ title: 'Projects' }} />
<Main>
bye
</Main>
<Footer />
</>
)
}

4188
pnpm-lock.yaml Normal file

File diff suppressed because it is too large Load diff

View file

@ -1,6 +0,0 @@
module.exports = {
plugins: {
tailwindcss: {},
autoprefixer: {},
},
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 107 KiB

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

BIN
public/favicon_dark.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

BIN
public/favicon_light.ico Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 108 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 104 KiB

3
public/icons/logo.svg Normal file
View file

@ -0,0 +1,3 @@
<svg width="24" height="24" viewBox="0 0 24 24" fill="currentColor" xmlns="http://www.w3.org/2000/svg">
<path d="M5.6375 18.3766C4.85417 17.5932 4.25 16.6807 3.825 15.6391C3.4 14.5974 3.1875 13.5516 3.1875 12.5016C3.1875 11.2349 3.4125 10.0724 3.8625 9.01406C4.3125 7.95573 4.97083 7.00156 5.8375 6.15156C6.50417 5.46823 7.32917 4.91823 8.3125 4.50156C9.29583 4.0849 10.4167 3.76823 11.675 3.55156C12.9333 3.3349 14.3167 3.2099 15.825 3.17656C17.3333 3.14323 18.9292 3.17656 20.6125 3.27656C20.7625 4.9599 20.8292 6.5474 20.8125 8.03906C20.7958 9.53073 20.6833 10.9141 20.475 12.1891C20.2667 13.4641 19.9458 14.6099 19.5125 15.6266C19.0792 16.6432 18.5208 17.5016 17.8375 18.2016C16.9708 19.0682 16.0208 19.7266 14.9875 20.1766C13.9542 20.6266 12.8625 20.8516 11.7125 20.8516C10.4958 20.8516 9.39167 20.6557 8.4 20.2641C7.40833 19.8724 6.4875 19.2432 5.6375 18.3766ZM8.4875 18.0516C8.92083 18.3349 9.41667 18.5516 9.975 18.7016C10.5333 18.8516 11.1042 18.9266 11.6875 18.9266C12.5542 18.9266 13.4042 18.7432 14.2375 18.3766C15.0708 18.0099 15.8208 17.4849 16.4875 16.8016C16.9708 16.2849 17.3792 15.6182 17.7125 14.8016C18.0458 13.9849 18.2958 13.0641 18.4625 12.0391C18.6292 11.0141 18.7333 9.9099 18.775 8.72656C18.8167 7.54323 18.8125 6.3349 18.7625 5.10156C17.2292 5.06823 15.8458 5.08906 14.6125 5.16406C13.3792 5.23906 12.2833 5.37656 11.325 5.57656C10.3667 5.77656 9.55417 6.0349 8.8875 6.35156C8.22083 6.66823 7.67917 7.0349 7.2625 7.45156C6.5625 8.15156 6.02917 8.8974 5.6625 9.68906C5.29583 10.4807 5.1125 11.2932 5.1125 12.1266C5.1125 12.8932 5.275 13.7057 5.6 14.5641C5.925 15.4224 6.3375 16.1182 6.8375 16.6516C7.67083 15.0516 8.7 13.6141 9.925 12.3391C11.15 11.0641 12.4958 10.0349 13.9625 9.25156C12.5292 10.5182 11.3625 11.8766 10.4625 13.3266C9.5625 14.7766 8.90417 16.3516 8.4875 18.0516V18.0516Z" />
</svg>

After

Width:  |  Height:  |  Size: 1.8 KiB

View file

@ -0,0 +1,242 @@
@import 'variables.module';
.consentBanner {
position: fixed;
right: 16px;
@media (max-width: 768px) {
left: 16px;
}
bottom: 16px;
padding: 16px;
border-radius: 16px;
border: 1px solid rgba($colorBorderLight2, 0.3);
background: $colorSurfaceLight2;
display: flex;
flex-direction: column;
justify-content: center;
align-items: flex-end;
gap: 16px;
max-width: 700px;
z-index: 5;
p {
color: $colorTextLight1;
&:not(:last-child) {
margin-bottom: 4px;
}
}
@media (prefers-color-scheme: dark) {
background: $colorSurfaceDark2;
border-color: rgba($colorBorderDark2, 0.3);
p {
color: $colorTextDark1;
}
}
}
.card {
background: $colorSurfaceLight2;
border-radius: 16px;
border: 1px solid rgba($colorBorderLight2, 0.3);
padding: 16px;
@media (prefers-color-scheme: dark) {
background: $colorSurfaceDark2;
border-color: rgba($colorBorderDark2, 0.3);
}
}
.progressBar {
width: 100%;
height: 10px;
background: $colorSurfaceLight3;
border: 1px solid rgba($colorBorderLight3, 0.3);
border-radius: 8px;
overflow: hidden;
@media (prefers-color-scheme: dark) {
background: $colorSurfaceDark3;
border-color: rgba($colorBorderDark3, 0.3);
}
> .progressBarProgress {
height: 100%;
background: $colorPrimary;
}
}
.button {
display: flex;
justify-content: center;
align-items: center;
gap: 8px;
padding: 8px 16px;
border-radius: 40px;
border: none;
cursor: pointer;
font-family: 'Rethink Sans', 'Noto Color Emoji', 'Noto Emoji', sans-serif;
font-weight: 600;
font-size: 0.9rem;
transition: background 0.1s ease-in-out;
&[data-type="primary"] {
background: #0F766E;
color: $colorTextDark1;
&:hover {
background: #115e59;
}
}
&[data-type="text"] {
background: transparent;
color: $colorTextLight1;
&:hover {
background: rgba($colorSurfaceLight4, 0.7);
}
@media (prefers-color-scheme: dark) {
color: $colorTextDark1;
&:hover {
background: rgba($colorSurfaceDark4, 0.7);
}
}
}
&[data-type="icon"] {
background: transparent;
color: $colorTextLight1;
padding: 8px;
&:hover {
background: rgba($colorSurfaceLight4, 0.7);
}
@media (prefers-color-scheme: dark) {
color: $colorTextDark1;
&:hover {
background: rgba($colorSurfaceDark4, 0.7);
}
}
}
}
.dialog {
position: fixed;
top: 0;
left: 0;
width: 100%;
height: 100%;
background: rgba(0, 0, 0, 0.5);
display: flex;
justify-content: center;
align-items: center;
z-index: 15;
> .card {
min-width: 600px;
> .dialogHeader {
display: flex;
justify-content: space-between;
align-items: center;
margin-bottom: 1rem;
> label {
font-size: 1.3rem;
font-weight: 700;
}
}
}
}
.inputText {
padding: .5rem 1rem;
margin-bottom: .5rem;
border-radius: 8px;
border: 1px solid rgba($colorBorderLight3, 0.3);
width: 100%;
background: $colorSurfaceLight3;
font-family: 'Rethink Sans', 'Noto Color Emoji', 'Noto Emoji', sans-serif;
font-size: 1rem;
color: $colorTextLight1;
transition: border 0.1s ease-in-out;
&:focus {
border-color: $colorPrimary;
outline: none;
}
@media (prefers-color-scheme: dark) {
background: $colorSurfaceDark3;
border-color: rgba($colorBorderDark3, 0.3);
color: $colorTextDark1;
}
}
textarea.inputText {
resize: vertical;
min-height: 250px;
}
.separatedButtonGroup {
display: flex;
gap: 8px;
}

15
styles/Footer.module.scss Normal file
View file

@ -0,0 +1,15 @@
@import 'global.module';
.footer {
background: $colorSurfaceLight2;
border-top: 1px solid rgba($colorBorderLight2, 0.3);
padding: 16px;
@media (prefers-color-scheme: dark) {
background: $colorSurfaceDark2;
border-color: rgba($colorBorderDark2, 0.3);
}
}

82
styles/Home.module.scss Normal file
View file

@ -0,0 +1,82 @@
@import 'global.module';
.hero {
height: 100svh;
padding-top: 57px;
padding-bottom: 1rem;
> .container {
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
height: 100%;
> .heroTop {
display: flex;
justify-content: center;
flex-direction: column;
flex-grow: 1;
width: 100%;
> h1 {
font-weight: 700;
margin-bottom: .5rem;
}
> .socialIcons {
margin-top: 1rem;
display: flex;
list-style: none;
gap: 8px;
}
}
}
}
.skillsGrid {
display: grid;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
gap: 1rem;
> .skillCard {
display: flex;
align-items: center;
gap: 0.5rem;
> .skillInner {
flex-grow: 1;
display: flex;
flex-direction: column;
justify-content: space-between;
height: 100%;
> .skillLabel {
display: flex;
justify-content: space-between;
align-items: center;
}
}
}
}

162
styles/Navbar.module.scss Normal file
View file

@ -0,0 +1,162 @@
@import 'global.module';
.navBar {
position: fixed;
top: 0;
right: 0;
left: 0;
transition: background 0.2s ease-in-out, border 0.2s ease-in-out, backdrop-filter 0.2s ease-in-out;
border-bottom: 1px solid transparent;
z-index: 10;
> .container {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.5rem 0;
> .navLogoContainer {
flex-grow: 1;
display: flex;
align-items: center;
justify-content: space-between;
> .navLogo {
fill: #0F766E;
}
> .navToggle {
display: none;
@media (max-width: 768px) {
display: flex;
}
}
}
> .navCollapse {
display: flex;
align-items: center;
justify-content: space-between;
flex-grow: 1;
> ul {
list-style: none;
display: flex;
gap: 2px;
&.navLinks {
> li {
> a {
color: rgba($colorTextLight1, 0.75);
text-decoration: none;
padding: 8px 16px;
transition: color 0.1s ease-in-out;
&:hover, &.active {
color: rgba($colorTextLight1, 1);
}
@media (prefers-color-scheme: dark) {
color: rgba($colorTextDark1, 0.75);
&:hover, &.active {
color: rgba($colorTextDark1, 1);
}
}
}
}
}
}
&.open {
display: flex;
}
@media (max-width: 768px) {
display: none;
}
}
@media (max-width: 768px) {
padding: 0.5rem 1rem;
}
}
&.navBarScrolled, &.open {
background: $colorSurfaceLight2;
border-bottom: 1px solid rgba($colorBorderLight2, 0.3);
@media (prefers-color-scheme: dark) {
background: $colorSurfaceDark2;
border-bottom: 1px solid rgba($colorBorderDark2, 0.3);
}
}
&.open {
height: 100vh;
background: rgba($colorSurfaceLight2, 0.95);
backdrop-filter: blur(0.5rem);
@media (prefers-color-scheme: dark) {
background: rgba($colorSurfaceDark2, 0.95);
}
> .container {
flex-direction: column;
height: 100%;
> .navLogoContainer {
width: 100%;
flex-grow: 0;
margin-bottom: 0.5rem;
}
> .navCollapse {
width: 100%;
flex-grow: 1;
flex-direction: column;
align-items: end;
justify-content: center;
gap: 2rem;
> .navLinks {
flex-grow: 0;
flex-direction: column;
align-items: end;
> li {
padding: .5rem 0 .5rem 1rem;
}
}
}
}
}
}

12
styles/global.module.scss Normal file
View file

@ -0,0 +1,12 @@
@import 'variables.module';
.container {
max-width: 1100px;
margin: 0 auto;
@media (max-width: 1130px) {
padding-left: 16px;
padding-right: 16px;
}
}

View file

@ -1,13 +1,67 @@
@import url('https://fonts.googleapis.com/css2?family=Fira+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Noto+Color+Emoji&family=Noto+Sans+Mono:wght@100;200;300;400;500;600;700;800;900&family=Noto+Sans:ital,wght@0,100;0,200;0,300;0,400;0,500;0,600;0,700;0,800;0,900;1,100;1,200;1,300;1,400;1,500;1,600;1,700;1,800;1,900&family=Outfit:wght@100;200;300;400;500;600;700;800;900&display=swap');
@use 'sass:meta';
@tailwind base;
@tailwind components;
@tailwind utilities;
@import url('https://fonts.googleapis.com/css2?family=Fira+Code:wght@300;400;500;600;700&family=Noto+Color+Emoji&family=Noto+Emoji:wght@300;400;500;600;700&family=Rethink+Sans:ital,wght@0,400;0,500;0,600;0,700;0,800;1,400;1,500;1,600;1,700;1,800&display=swap');
@import 'variables.module';
* {
padding: 0;
margin: 0;
box-sizing: border-box;
scrollbar-color: $colorPrimary $colorSurfaceLight1;
@media (prefers-color-scheme: dark) {
scrollbar-color: $colorPrimary $colorSurfaceDark1;
}
}
html {
min-height: 100svh;
scroll-behavior: smooth;
scroll-padding-top: calc(57px + 1rem);
}
body {
background: $colorSurfaceLight1;
color: $colorTextLight1;
font-family: 'Rethink Sans', 'Noto Color Emoji', 'Noto Emoji', sans-serif;
min-height: 100%;
@media (prefers-color-scheme: dark) {
background: $colorSurfaceDark1;
color: $colorTextDark1;
}
}
a {
text-decoration: none;
}
h1 {
font-size: 2.5rem;
}
h2 {
font-size: 2rem;
}
main {
> div > div {
> h2 {
margin-bottom: 1rem;
text-align: center;
}
&:not(:last-child) {
margin-bottom: 1rem;
}
}
margin-bottom: 1rem;
}
/*!
Theme: Google Dark
Author: Seth Wright (http://sethawright.com)
License: ~ MIT (or more permissive) [via base16-schemes-source]
Maintainer: @highlightjs/core-team
Version: 2021.09.0
*/.hljs-comment{color:#969896}.hljs-tag{color:#b4b7b4}.hljs-operator,.hljs-punctuation,.hljs-subst{color:#c5c8c6}.hljs-operator{opacity:.7}.hljs-bullet,.hljs-deletion,.hljs-name,.hljs-selector-tag,.hljs-template-variable,.hljs-variable{color:#cc342b}.hljs-attr,.hljs-link,.hljs-literal,.hljs-number,.hljs-symbol,.hljs-variable.constant_{color:#f96a38}.hljs-class .hljs-title,.hljs-title,.hljs-title.class_{color:#fba922}.hljs-strong{font-weight:700;color:#fba922}.hljs-addition,.hljs-code,.hljs-string,.hljs-title.class_.inherited__{color:#198844}.hljs-attribute,.hljs-built_in,.hljs-doctag,.hljs-function .hljs-title,.hljs-keyword.hljs-atrule,.hljs-quote,.hljs-regexp,.hljs-section,.hljs-title.function_,.ruby .hljs-property{color:#3971ed}.diff .hljs-meta,.hljs-keyword,.hljs-template-tag,.hljs-type{color:#a36ac7}.hljs-emphasis{color:#a36ac7;font-style:italic}.hljs-meta,.hljs-meta .hljs-keyword,.hljs-meta .hljs-string{color:#3971ed}.hljs-meta .hljs-keyword,.hljs-meta-keyword{font-weight:700}

View file

@ -0,0 +1,27 @@
$colorPrimary: hsl(175, 77%, 26%);
$colorTextLight1: hsl(175, 77%, 10%);
$colorTextLight2: hsl(175, 30%, 30%);
$colorSurfaceLight1: hsl(175, 25%, 90%);
$colorSurfaceLight2: hsl(175, 20%, 95%);
$colorSurfaceLight3: hsl(175, 20%, 92%);
$colorSurfaceLight4: hsl(175, 20%, 85%);
$colorBorderLight1: hsl(175, 20%, 80%);
$colorBorderLight2: hsl(175, 20%, 70%);
$colorBorderLight3: hsl(175, 20%, 60%);
$colorBorderLight4: hsl(175, 20%, 50%);
$colorTextDark1: hsl(175, 15%, 85%);
$colorTextDark2: hsl(175, 5%, 65%);
$colorSurfaceDark1: hsl(175, 10%, 10%);
$colorSurfaceDark2: hsl(175, 10%, 15%);
$colorSurfaceDark3: hsl(175, 5%, 20%);
$colorSurfaceDark4: hsl(175, 5%, 25%);
$colorBorderDark1: hsl(175, 5%, 30%);
$colorBorderDark2: hsl(175, 5%, 40%);
$colorBorderDark3: hsl(175, 5%, 50%);
$colorBorderDark4: hsl(175, 5%, 60%);

View file

@ -1,20 +0,0 @@
/** @type {import('tailwindcss').Config} */
module.exports = {
content: [
'./pages/**/*.{js,ts,jsx,tsx,mdx}',
'./components/**/*.{js,ts,jsx,tsx,mdx}',
'./app/**/*.{js,ts,jsx,tsx,mdx}',
],
theme: {
fontFamily: {
sans: ['"Noto Sans"', '"Noto Color Emoji"', 'system-ui'],
serif: ['Outfit', '"Noto Color Emoji"', 'system-ui'],
mono: ['"Noto Sans Mono"', 'monospace'],
},
},
plugins: [
require('@tailwindcss/typography'),
require('@tailwindcss/forms'),
require('tailwind-scrollbar'),
],
}