Update design - use Sveltekit (#28)

* first push

* feature cards, hall of fame

* playing around

* community slice

* home

* mobile home

* wtf happend with the git

* something

* stuff

* stuff

* remove blur on nav on mobile

* wall of fame, improve performance

* always use current year for copyright

* close navbar on navigation for mobile

* improve performance community slice

* improve performance features slice

* prepare performance improvement for rice thumbnails

* .

* .

* more stuff

* new hero effect experiment

* .

* work

* .

* .

* more stuf

* hide discord profile image if image is not loaded yet

* update links to rices

* .

* fix postinstall not running

* .

* remove quotes

* remove .gitattributes

* update readme

* Footer: Fix bottom margin

* HeroBackground: Remove images+animations

* Remove discord

* InstallSection: Change button label

* Revert "Remove discord"

This reverts commit 3e3eab848c.

* Revert "HeroBackground: Remove images+animations"

This reverts commit 4979bf2a39.

* improve hero rice

* small improvments

* small css adjustments

* update Hero

* upgrade dependencies

* fix prettier

* improve navbar on mobile

* add top ricers to community slice

* improve hero

* improve preview slice

* add plugins section

* Add OpenSus

* set min width for quotes in discord profiles

* update nix install command

* improve patternbackground mouse follow

* improve pattern mouse follow

* fix play button for autoplay blocked users

* pluginsSlice, sync videos playback state

* add fullscreen button to videos

* clean up package.json

* slide pluginsSlice video on hover on desktop

* PluginsSlice: small improvments

* priotize loading logo

* add meta tags

* previewRice: make fullscreen button bigger

* pluginSlice fix fullscreen btn with sliding

* update readme

* fix typo

* ssr logo as svg
Use the svg directly so that it does not need to get fetched anymore

* pluginsSlice: animate in

* communityslice: sort by size

* communityslice: add a profile

* walloffame cleanup

* WallOfFame: Nicer wisdoms

* add grain to gradients

* decrease grain strength

* remove unused imports

* add space winners

* small clean up

* use aylur settings video

* increase grain strength

* fix grain path

* contest: date as pill

* famedrice: colored titles

* update Aylurs video

* pluginsSlice: decrease video gap

* pluginsSlice: improve mobile padding

* pluginSlice: improve play btn position

* installSlice: improve padding on mobile

* add flick0s rice as honorable mention

* rename WoF to HoF

* add Slackware

* Revert "add Slackware"

This reverts commit defc756cc6.

* Revert "add flick0s rice as honorable mention"

This reverts commit 8cffcad8cb.

* add outfoxxeds plugin video

* pluginSlice: update outfoxxed caption

* pluginSlice: outfoxxed video thumb

* pluginsSlice: refractor, add outfoxxed thumbnail

* video: Fix autoplay detection for Firefox

* Video: Fix poster size

* reencode aylurs file bc firefox errors

* PreviewRice: refactor

* remove console.log

* PluginsSlice: fix playback sync logic

* update readme

* fix typo

* also use more modern encoding for videos

* pluginsSlice: Improve outfoxxed caption

* InstallSlice: Shorten NixOs command for formatting

* HoF: Fix japane decorative title on mobile

* PluginsSlice: Improve video display on mobile

* PluginsSlice: Improve caption padding on mobile

* PluginsSlice: outfoxxed all lowercase

* fix typ

* remove no-index

* HoF: Update japanese wisdom

* remove unused dependency

* fix grammar

* blurredImages: add shebang

* HoF: Use data file to generate rices

* use discord link as constant

* heroBgTile: smooth fade-out of img

* Video: Add pause button

* pluginSlice: add slide in button

* remove  AV1 webm video  and only use h264 mp4

* video: small play button plays video

* pluginsSlice: nicer slide button

* reeconde end_4 rice, delete unused AV1 video files

* HeroBackground: use transition for artwork tiles
This commit is contained in:
Visual-Dawg 2023-11-25 22:31:55 +02:00 committed by GitHub
parent 275a268af4
commit 6ec7cfa1b8
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
191 changed files with 7247 additions and 4333 deletions

13
.eslintignore Executable file
View file

@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

19
.eslintrc.cjs Executable file
View file

@ -0,0 +1,19 @@
module.exports = {
root: true,
extends: ['eslint:recommended', 'plugin:svelte/recommended', 'prettier'],
parserOptions: {
sourceType: 'module',
ecmaVersion: 'latest',
extraFileExtensions: ['.svelte']
},
env: {
browser: true,
es2017: true,
node: true
},
rules: {
'no-unused-vars': ['off', { varsIgnorePattern: '.*' }],
'svelte/no-at-html-tags': 'off'
},
globals: { globalThis: true }
}

8
.gitignore vendored Normal file → Executable file
View file

@ -1,3 +1,11 @@
old
.svelte-kit
build
# For the generated blurred images
**/generated_*
# Logs
logs
*.log

2
.npmrc Executable file
View file

@ -0,0 +1,2 @@
engine-strict=true
resolution-mode=highest

13
.prettierignore Executable file
View file

@ -0,0 +1,13 @@
.DS_Store
node_modules
/build
/.svelte-kit
/package
.env
.env.*
!.env.example
# Ignore files for PNPM, NPM and YARN
pnpm-lock.yaml
package-lock.json
yarn.lock

3
.vscode/extensions.json vendored Executable file
View file

@ -0,0 +1,3 @@
{
"recommendations": ["bradlc.vscode-tailwindcss", "svelte.svelte-vscode"]
}

0
LICENSE Normal file → Executable file
View file

20
README.md Normal file → Executable file
View file

@ -2,12 +2,26 @@
The hyprland.org website files. See it on [hyprland.org](https://hyprland.org)
If you are here to Contribute to the Wiki, [click here](https://github.com/hyprwm/hyprland-wiki).
> [!NOTE]
> If you are here to contribute to the Wiki, [click here](https://github.com/hyprwm/hyprland-wiki).
## Contributions
feel free
Feel free
## Requirements
- `pnpm` for the package management. (npm works too if you only want to build it)
- `imagemagick` to generate the blurred background images.
## Development
- `pnpm install` To install the required npm packages
- `pnpm dev` To start the dev server
- `pnpm build` To build the static site into `./build`
- `pnpm preview` To preview the builded site in `./build` ( no dev environment )
## Credits
[System-x64](https://github.com/System-x64) - for the original site code
- [VDawg](https://github.com/Visual-Dawg) - for the new site and design.
- [System-x64](https://github.com/System-x64) - for the original site code

View file

@ -1,48 +0,0 @@
const express = require("express");
const app = express();
const { join } = require("path");
const compression = require("compression");
const expressEJSLayouts = require("express-ejs-layouts");
app.disable('x-powered-by');
app.use(compression());
app.use(express.static(join(__dirname, "public")));
app.set("view engine", "ejs");
app.set("views", join(__dirname, "views"));
app.set("layout extractScripts", true);
app.set("layout extractStyles", true);
app.use(expressEJSLayouts);
app.use((req, res, next) => {
// Security Headers - Refer to MDN and helmetjs docs
res.set("Content-Security-Policy", `default-src 'self'; img-src 'self'; media-src 'self'; script-src 'unsafe-inline' 'self' https://cdn.jsdelivr.net https://code.jquery.com; style-src 'self' 'unsafe-inline' https://cdn.jsdelivr.net; font-src 'self' data:; object-src 'none'; child-src 'none'; frame-ancestors 'none'; frame-src 'none'; upgrade-insecure-requests`);
res.set("Strict-Transport-Security", "max-age=15552000; includeSubDomains");
res.set("X-Content-Type-Options", "nosniff");
res.set("X-Frame-Options", "DENY");
next();
});
app.get("/", (req, res) => {
res.render("home");
});
app.get("/rices", (req, res) => {
res.render("rices");
});
app.get("/discord", (req, res) => {
res.status(200).send("<head><title>redirecting...</title><body><script>window.location.href='https://discord.gg/hQ9XvMUjjr';</script></body>");
});
app.use((_, res) => {
res.status(404).render("404");
});
app.listen(process.env.PORT || 4000, () => {
console.log("Listening to PORT: 4000");
});

17
jsconfig.json Executable file
View file

@ -0,0 +1,17 @@
{
"extends": "./.svelte-kit/tsconfig.json",
"compilerOptions": {
"allowJs": true,
"checkJs": false,
"esModuleInterop": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"skipLibCheck": true,
"sourceMap": true,
"strict": true
}
// Path aliases are handled by https://kit.svelte.dev/docs/configuration#alias and https://kit.svelte.dev/docs/configuration#files
//
// If you want to overwrite includes/excludes, make sure to copy over the relevant includes/excludes
// from the referenced tsconfig.json - TypeScript does not merge them in
}

1978
package-lock.json generated

File diff suppressed because it is too large Load diff

49
package.json Normal file → Executable file
View file

@ -1,24 +1,20 @@
{
"name": "hyprland-website",
"version": "1.1.0",
"description": "Website for Hyprland - Hyprland - A wayland compositor that doesn't sacrifice on its looks!",
"main": "index.js",
"description": "Website for Hyprland - Hyprland - A wayland compositor with the looks.",
"repository": "github:hyprwm/hyprland-website",
"scripts": {
"start": "NODE_ENV=production node index.js",
"dev": "nodemon index.js"
"dev": "vite dev",
"build": "vite build",
"preview": "vite preview",
"format": "prettier --plugin-search-dir . --write .",
"postinstall": "./scripts/generate-blurred-images.sh"
},
"keywords": [
"hyprland"
],
"author": "",
"license": "BSD-3-Clause",
"dependencies": {
"compression": "^1.7.4",
"ejs": "^3.1.8",
"express": "^4.18.1",
"express-ejs-layouts": "^2.5.1"
},
"repository": "github:hyprwm/hyprland-website",
"private": "true",
"os": [
"darwin",
@ -28,6 +24,35 @@
"node": ">=16.0.0"
},
"devDependencies": {
"nodemon": "^2.0.20"
"@iconify/json": "^2.2.140",
"@interactjs/types": "^1.10.20",
"@sveltejs/adapter-static": "^2.0.3",
"@sveltejs/kit": "^1.27.4",
"autoprefixer": "^10.4.16",
"eslint": "^8.53.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-svelte": "^2.35.0",
"postcss": "^8.4.31",
"prettier": "^3.0.3",
"prettier-plugin-svelte": "^3.1.0",
"svelte": "^4.2.3",
"svelte-check": "^3.6.0",
"tailwindcss": "^3.3.5",
"tailwindcss-animate": "^1.0.7",
"typescript": "^5.2.2",
"unplugin-icons": "^0.17.3",
"vite": "^4.5.0"
},
"type": "module",
"dependencies": {
"@fontsource-variable/work-sans": "^5.0.16",
"@fontsource/ibm-plex-mono": "^5.0.8",
"clsx": "^2.0.0",
"interactjs": "^1.10.20",
"prettier-plugin-tailwindcss": "^0.5.7",
"remeda": "^1.29.0",
"rxjs": "^7.8.1",
"svelte-inview": "^4.0.1",
"ts-pattern": "^5.0.5"
}
}

2732
pnpm-lock.yaml Normal file → Executable file

File diff suppressed because it is too large Load diff

7
postcss.config.js Executable file
View file

@ -0,0 +1,7 @@
export default {
plugins: {
'tailwindcss/nesting': {},
tailwindcss: {},
autoprefixer: {}
}
}

9
prettier.config.cjs Executable file
View file

@ -0,0 +1,9 @@
module.exports = {
useTabs: true,
singleQuote: true,
semi: false,
trailingComma: 'none',
printWidth: 100,
plugins: ['prettier-plugin-svelte', 'prettier-plugin-tailwindcss'],
tailwindFunctions: ['clsx']
}

View file

@ -1,45 +0,0 @@
.con404 {
margin: auto;
text-align: center;
color: #cfe8f6;
font-family: "LondonBetween";
}
.con404>img {
margin-top: 60px;
margin-bottom: 60px;
margin-left: auto;
margin-right: auto;
max-height: 40vh;
max-width: 60vw;
}
.con404>h1 {
margin-bottom: 20px;
font-size: 48px;
}
.con404>p {
font-size: 20px;
margin-left: 30px;
margin-right: 30px;
margin-bottom: 30px;
}
.con404>a>button {
padding: 20px;
background-color: aquamarine;
border: solid aquamarine;
border-radius: 0.2rem;
margin-bottom: 60px;
transition: all 0.5s ease;
font-size: 20px;
font-family: "LondonBetween";
}
.con404>a>button:hover {
background-color: #1a1a2e;
color: aquamarine;
transform: scale(1.2);
cursor: pointer;
}

File diff suppressed because it is too large Load diff

View file

@ -1,19 +0,0 @@
#mob-github {
color: #1a1a2e;
background-color: aquamarine;
/* width: 130px;
height: 50px; */
padding: 20px 30px;
text-align: center;
vertical-align: center;
margin-top: 20px ;
border-radius: 0.2rem;
}
#mob-github:hover {
transform: scale(1.2);
background-color: #1a1a2e;
color: aquamarine;
border: 4px solid aquamarine;
}

View file

@ -1,61 +0,0 @@
.wofdiv {
margin: auto;
text-align: center;
color: #cfe8f6;
}
.wofdiv>h1 {
font-family: "LondonBetween";
font-size: 48px;
margin-top: 60px;
margin-bottom: 30px;
}
.wofdiv>p {
font-size: 20px;
font-family: "LondonBetween";
margin: 0 10%;
margin-bottom: 60px;
}
.bigtext {
font-family: "LondonBetween";
color: #cfe8f6;
font-size: 5rem;
}
.ricewins {
text-align: center;
margin: auto;
margin-bottom: 60px;
}
div.ricewins>ul {
list-style-type: none;
}
.month {
font-family: "LondonBetween";
color: #cfe8f6;
margin: 30px;
}
.win-text {
font-size: 24px;
color: #cfe8f6;
font-family: "LondonBetween";
margin: 20px;
}
li.win-text>img {
max-width: 60vw;
}
@media only screen and (max-width: 1000px) {
li.win-text>img {
max-width: 80vw;
min-width: 80vw;
;
}
}

Binary file not shown.

Before

Width:  |  Height:  |  Size: 606 B

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.3 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 17 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 76 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 980 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 903 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 595 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 593 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.3 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 75 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.7 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 2.4 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 817 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 607 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 3.1 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 629 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 642 KiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 1.2 MiB

Binary file not shown.

Before

Width:  |  Height:  |  Size: 48 KiB

View file

@ -1,12 +0,0 @@
const doc = document;
const menuOpen = doc.querySelector(".mob-menu");
const menuClose = doc.querySelector(".close");
const overlay = doc.querySelector(".overlay");
menuOpen.addEventListener("click", () => {
overlay.classList.add("overlay--active");
});
menuClose.addEventListener("click", () => {
overlay.classList.remove("overlay--active");
});

View file

@ -1,15 +0,0 @@
$(document).ready(function () {
$(".animatedVideo").each(function () {
$(this).get(0).pause();
});
$(window).on("scroll", function () {
$(".animatedVideo").each(function () {
let scroll = $(window).scrollTop();
let elementTop = $(this).offset().top;
let elementHeight = $(this).height();
if (scroll > elementTop - $(window).height() + elementHeight) {
$(this).get(0).play();
}
});
});
});

View file

@ -1,11 +0,0 @@
const links = document.querySelectorAll(".like-this");
links.forEach((link) => {
link.addEventListener("click", (event) => {
event.preventDefault();
const targetId = link.getAttribute("href");
const target = document.querySelector(targetId);
target.scrollIntoView({ behavior: "smooth" });
});
});

View file

@ -1,17 +0,0 @@
const swiper = new Swiper(".swiper", {
autoplay: {
delay: 2000,
disableOnInteraction: true,
},
loop: true,
pagination: {
el: ".swiper-pagination",
clickable: true,
},
navigation: {
nextEl: ".swiper-button-next",
prevEl: ".swiper-button-prev",
},
});

View file

@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-box-arrow-in-down" viewBox="0 0 16 16">
<path fill-rule="evenodd" d="M3.5 6a.5.5 0 0 0-.5.5v8a.5.5 0 0 0 .5.5h9a.5.5 0 0 0 .5-.5v-8a.5.5 0 0 0-.5-.5h-2a.5.5 0 0 1 0-1h2A1.5 1.5 0 0 1 14 6.5v8a1.5 1.5 0 0 1-1.5 1.5h-9A1.5 1.5 0 0 1 2 14.5v-8A1.5 1.5 0 0 1 3.5 5h2a.5.5 0 0 1 0 1h-2z" color="#cfe8f6"/>
<path fill-rule="evenodd" d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z" color="#cfe8f6"/>
</svg>

Before

Width:  |  Height:  |  Size: 585 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 12 KiB

View file

@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-terminal" viewBox="0 0 16 16">
<path color="#cfe8f6" d="M6 9a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3A.5.5 0 0 1 6 9zM3.854 4.146a.5.5 0 1 0-.708.708L4.793 6.5 3.146 8.146a.5.5 0 1 0 .708.708l2-2a.5.5 0 0 0 0-.708l-2-2z"/>
<path color="#cfe8f6" d="M2 1a2 2 0 0 0-2 2v10a2 2 0 0 0 2 2h12a2 2 0 0 0 2-2V3a2 2 0 0 0-2-2H2zm12 1a1 1 0 0 1 1 1v10a1 1 0 0 1-1 1H2a1 1 0 0 1-1-1V3a1 1 0 0 1 1-1h12z"/>
</svg>

Before

Width:  |  Height:  |  Size: 497 B

View file

@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-download" viewBox="0 0 16 16">
<path d="M.5 9.9a.5.5 0 0 1 .5.5v2.5a1 1 0 0 0 1 1h12a1 1 0 0 0 1-1v-2.5a.5.5 0 0 1 1 0v2.5a2 2 0 0 1-2 2H2a2 2 0 0 1-2-2v-2.5a.5.5 0 0 1 .5-.5z" color="#cfe8f6"/>
<path d="M7.646 11.854a.5.5 0 0 0 .708 0l3-3a.5.5 0 0 0-.708-.708L8.5 10.293V1.5a.5.5 0 0 0-1 0v8.793L5.354 8.146a.5.5 0 1 0-.708.708l3 3z" color="#cfe8f6"/>
</svg>

Before

Width:  |  Height:  |  Size: 459 B

View file

@ -1,5 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-gpu-card" viewBox="0 0 16 16">
<path d="M4 8a1.5 1.5 0 1 1 3 0 1.5 1.5 0 0 1-3 0Zm7.5-1.5a1.5 1.5 0 1 0 0 3 1.5 1.5 0 0 0 0-3Z"/>
<path d="M0 1.5A.5.5 0 0 1 .5 1h1a.5.5 0 0 1 .5.5V4h13.5a.5.5 0 0 1 .5.5v7a.5.5 0 0 1-.5.5H2v2.5a.5.5 0 0 1-1 0V2H.5a.5.5 0 0 1-.5-.5Zm5.5 4a2.5 2.5 0 1 0 0 5 2.5 2.5 0 0 0 0-5ZM9 8a2.5 2.5 0 1 0 5 0 2.5 2.5 0 0 0-5 0Z"/>
<path d="M3 12.5h3.5v1a.5.5 0 0 1-.5.5H3.5a.5.5 0 0 1-.5-.5v-1Zm4 1v-1h4v1a.5.5 0 0 1-.5.5h-3a.5.5 0 0 1-.5-.5Z"/>
</svg>

Before

Width:  |  Height:  |  Size: 575 B

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 506 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 6.2 KiB

File diff suppressed because one or more lines are too long

Before

Width:  |  Height:  |  Size: 462 KiB

View file

@ -1,4 +0,0 @@
<svg xmlns="http://www.w3.org/2000/svg" width="16" height="16" fill="currentColor" class="bi bi-exclamation-circle" viewBox="0 0 16 16">
<path d="M8 15A7 7 0 1 1 8 1a7 7 0 0 1 0 14zm0 1A8 8 0 1 0 8 0a8 8 0 0 0 0 16z"/>
<path d="M7.002 11a1 1 0 1 1 2 0 1 1 0 0 1-2 0zM7.1 4.995a.905.905 0 1 1 1.8 0l-.35 3.507a.552.552 0 0 1-1.1 0L7.1 4.995z"/>
</svg>

Before

Width:  |  Height:  |  Size: 354 B

Binary file not shown.

Binary file not shown.

Binary file not shown.

View file

@ -0,0 +1,30 @@
#!/usr/bin/env bash
parent_path=$( cd "$(dirname "${BASH_SOURCE[0]}")" ; pwd -P )
cd "$parent_path"
find "../static/imgs/ricing_competitions/" -type f \
\( -iname "*.jpg" -o -iname "*.png" -o -iname "*.gif" -o -iname "*.bmp" -o -iname "*.jpeg" -o -iname "*.webp" \) -not -name "generated_*" -print0 |
while IFS= read -r -d '' filepath; do
echo "$filepath" gets blurred
directory=$(dirname "$filepath")
filename=$(basename "$filepath")
generated_filename="${directory}/generated_${filename}"
brightness=$( convert $filepath -colorspace Gray -format "%[mean]" info: )
max_brightness="65535" # The possible maximum brightness possible from the previous command
brightness_threshold=$( python -c "print( $max_brightness * 0.5 )" )
# Boost the brightness if the image is very dark
brightness_boost=$( python -c "print( max( (1 - ($brightness / $brightness_threshold)) * 50 , 0) )" )
# Modify colors with LUT
magick convert -brightness-contrast ${brightness_boost}x40 -modulate 100,1000,100 "$filepath" "$generated_filename"
magick "$generated_filename" "./hald-clut.color.io.png" -hald-clut "$generated_filename"
# Also make them smaller to reduce file size
magick convert -modulate 100,250,100 -scale 10% -gaussian-blur 0x20 -resize 500% -quality 50 "$generated_filename" "$generated_filename"
# magick convert -scale 10% -brightness-contrast ${brightness_boost}x25 -modulate 100,500,100 -gaussian-blur 0x20 -resize 1000% "$filepath" "$generated_filename"
done

BIN
scripts/hald-clut.color.io.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

15
src/app.d.ts vendored Executable file
View file

@ -0,0 +1,15 @@
/// <reference types="@sveltejs/kit" />
/// <reference types="unplugin-icons/types/svelte" />
// See https://kit.svelte.dev/docs/types#app
// for information about these interfaces
declare global {
namespace App {
// interface Error {}
// interface Locals {}
// interface PageData {}
// interface Platform {}
}
}
export {}

14
src/app.html Executable file
View file

@ -0,0 +1,14 @@
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8" />
<link rel="icon" href="%sveltekit.assets%/favicon.ico" />
<meta name="viewport" content="width=device-width" />
<meta property="og:image" content="/imgs/og-img.png" />
<style></style>
%sveltekit.head%
</head>
<body data-sveltekit-preload-data="hover">
<div style="display: contents">%sveltekit.body%</div>
</body>
</html>

103
src/lib/Helper.mjs Executable file
View file

@ -0,0 +1,103 @@
/* eslint-disable no-useless-escape */
import { inview } from 'svelte-inview'
import { pick } from 'remeda'
import { Observable, debounceTime, share, startWith, throttleTime } from 'rxjs'
/**
* Fade: The initial opacity from 0 to 1.
*
* Zoom: The scale from 0 to 1.
*
* Slide: Slide in in pixels.
*
* @param {{fade?: number, zoom?: number, slide?: number, duration?: number, delay?: number, threshold?: number}} options
* @param { HTMLElement } node
* @returns
*/
export function animateIn(node, options) {
// Do nothing on mobile
if (getIsMobile()) return { destroy: () => undefined }
const observer = inview(node, { unobserveOnEnter: true, threshold: options.threshold ?? 0.4 })
options.duration ??= 840
const effects = Object.entries(pick(options, ['fade', 'zoom', 'slide', 'duration']))
const style = effects
.map(([effect, value]) => {
if (effect === 'slide') return `translate: 0px ${value}px`
if (effect === 'fade') return `opacity: ${value}`
if (effect === 'zoom') return `scale: ${value} ${value}`
if (effect === 'duration') {
return `transition: all ${value}ms`
}
})
.join(';')
node.style = style
let timeoutId
node.addEventListener('inview_enter', callback)
function callback() {
timeoutId = setTimeout(
effects.forEach(([effect]) => {
if (effect === 'slide') node.style.removeProperty('translate')
else if (effect === 'fade') node.style.removeProperty('opacity')
else if (effect === 'zoom') node.style.removeProperty('scale')
}),
options.delay ?? 0
)
}
return {
destroy: () => {
observer.destroy()
clearTimeout(timeoutId)
}
}
}
/**
* @param {number} start
* @param {number} end
* @param {number} given
* @returns
*/
export function lerp(start, end, given) {
return (1 - given) * start + given * end
}
/**
* Taken from https://stackoverflow.com/questions/11381673/detecting-a-mobile-browser/11381730#11381730
*/
export function getIsMobile() {
let check = false
;(function (a) {
if (
/(android|bb\d+|meego).+mobile|avantgo|bada\/|blackberry|blazer|compal|elaine|fennec|hiptop|iemobile|ip(hone|od)|iris|kindle|lge |maemo|midp|mmp|mobile.+firefox|netfront|opera m(ob|in)i|palm( os)?|phone|p(ixi|re)\/|plucker|pocket|psp|series(4|6)0|symbian|treo|up\.(browser|link)|vodafone|wap|windows ce|xda|xiino/i.test(
a
) ||
/1207|6310|6590|3gso|4thp|50[1-6]i|770s|802s|a wa|abac|ac(er|oo|s\-)|ai(ko|rn)|al(av|ca|co)|amoi|an(ex|ny|yw)|aptu|ar(ch|go)|as(te|us)|attw|au(di|\-m|r |s )|avan|be(ck|ll|nq)|bi(lb|rd)|bl(ac|az)|br(e|v)w|bumb|bw\-(n|u)|c55\/|capi|ccwa|cdm\-|cell|chtm|cldc|cmd\-|co(mp|nd)|craw|da(it|ll|ng)|dbte|dc\-s|devi|dica|dmob|do(c|p)o|ds(12|\-d)|el(49|ai)|em(l2|ul)|er(ic|k0)|esl8|ez([4-7]0|os|wa|ze)|fetc|fly(\-|_)|g1 u|g560|gene|gf\-5|g\-mo|go(\.w|od)|gr(ad|un)|haie|hcit|hd\-(m|p|t)|hei\-|hi(pt|ta)|hp( i|ip)|hs\-c|ht(c(\-| |_|a|g|p|s|t)|tp)|hu(aw|tc)|i\-(20|go|ma)|i230|iac( |\-|\/)|ibro|idea|ig01|ikom|im1k|inno|ipaq|iris|ja(t|v)a|jbro|jemu|jigs|kddi|keji|kgt( |\/)|klon|kpt |kwc\-|kyo(c|k)|le(no|xi)|lg( g|\/(k|l|u)|50|54|\-[a-w])|libw|lynx|m1\-w|m3ga|m50\/|ma(te|ui|xo)|mc(01|21|ca)|m\-cr|me(rc|ri)|mi(o8|oa|ts)|mmef|mo(01|02|bi|de|do|t(\-| |o|v)|zz)|mt(50|p1|v )|mwbp|mywa|n10[0-2]|n20[2-3]|n30(0|2)|n50(0|2|5)|n7(0(0|1)|10)|ne((c|m)\-|on|tf|wf|wg|wt)|nok(6|i)|nzph|o2im|op(ti|wv)|oran|owg1|p800|pan(a|d|t)|pdxg|pg(13|\-([1-8]|c))|phil|pire|pl(ay|uc)|pn\-2|po(ck|rt|se)|prox|psio|pt\-g|qa\-a|qc(07|12|21|32|60|\-[2-7]|i\-)|qtek|r380|r600|raks|rim9|ro(ve|zo)|s55\/|sa(ge|ma|mm|ms|ny|va)|sc(01|h\-|oo|p\-)|sdk\/|se(c(\-|0|1)|47|mc|nd|ri)|sgh\-|shar|sie(\-|m)|sk\-0|sl(45|id)|sm(al|ar|b3|it|t5)|so(ft|ny)|sp(01|h\-|v\-|v )|sy(01|mb)|t2(18|50)|t6(00|10|18)|ta(gt|lk)|tcl\-|tdg\-|tel(i|m)|tim\-|t\-mo|to(pl|sh)|ts(70|m\-|m3|m5)|tx\-9|up(\.b|g1|si)|utst|v400|v750|veri|vi(rg|te)|vk(40|5[0-3]|\-v)|vm40|voda|vulc|vx(52|53|60|61|70|80|81|83|85|98)|w3c(\-| )|webc|whit|wi(g |nc|nw)|wmlb|wonu|x700|yas\-|your|zeto|zte\-/i.test(
a.substr(0, 4)
)
)
check = true
})(navigator.userAgent || navigator.vendor || window.opera)
return check
}
/** Get the `generated_<filename>` if blurredThumbnail is not set manually **/
export function getBlurredPath(path) {
return `${path.substring(0, path.lastIndexOf('/'))}/generated_${path.split('/').at(-1)}`
}
/** Get a random item from an array */
export function getRandom(array) {
return array.at(Math.floor(Math.random() * array.length))
}

View file

@ -0,0 +1,162 @@
<script>
import {
BehaviorSubject,
Subject,
combineLatest,
debounceTime,
delay,
distinctUntilChanged,
filter,
map,
of,
startWith,
switchMap,
timer
} from 'rxjs'
import { onDestroy, onMount } from 'svelte'
import { spring } from 'svelte/motion'
/** The start position of the gradient. */
export let startPosition = [-1000, -1000]
/** @type {HTMLDivElement}*/
let wrapperElement = undefined
/** @type {import('rxjs').BehaviorSubject<boolean>}*/
const isMouseOver$ = new BehaviorSubject(false).pipe(
// Do not harshly stop updating the gradient when the mouse leaves, but wait a bit
switchMap((isTrue) => (isTrue ? of(isTrue) : timer(1500).pipe(map(() => false)))),
distinctUntilChanged()
)
/** @type {import('rxjs').BehaviorSubject<number>}*/
const gradientSize$ = new BehaviorSubject().pipe(
// Debounce resize events with some high number for performance
debounceTime(1),
map(() => wrapperElement.getBoundingClientRect().width * 3),
startWith(800)
)
/** @type {import('rxjs').Subject< {clientX: number, clientY: number} >}*/
const mousePosition$ = new Subject()
const gradientPosition$ = combineLatest([mousePosition$, gradientSize$, isMouseOver$]).pipe(
filter(([_, __, isMouseOver]) => isMouseOver),
map(([{ clientX, clientY }, gradientSize]) => {
const { x, y } = wrapperElement?.getBoundingClientRect() ?? { x: 0, y: 0 }
return [clientX - x - gradientSize * 0.5, clientY - y - gradientSize * 0.5]
}),
startWith(startPosition)
)
const gradientWiggle = spring(startPosition, { damping: 0.95, stiffness: 0.1 })
const subscription = gradientPosition$.subscribe((data) => gradientWiggle.set(data))
onDestroy(() => {
subscription.unsubscribe()
})
let hasJustMounted = true
onMount(() => {
resizeGradient()
hasJustMounted = false
})
$: {
if (!$isMouseOver$) {
globalThis.document?.removeEventListener('mousemove', track)
}
}
function resizeGradient() {
if (hasJustMounted || !isMouseOver$) return
gradientSize$.next()
}
function startTrackingMouse() {
if ($isMouseOver$) return
globalThis.document?.addEventListener('mousemove', track)
}
function track({ clientX, clientY }) {
mousePosition$.next({ clientX, clientY })
}
onDestroy(() => {
globalThis.document?.removeEventListener('mousemove', track)
})
</script>
<svelte:window on:resize={resizeGradient} />
<div
class={$$props.class + ' wrapper'}
on:mouseenter={startTrackingMouse}
on:mouseleave={({ clientX, clientY, currentTarget }) => {
const { x, width, y, height } = currentTarget.getBoundingClientRect()
const isMouseStillOver =
x <= clientX && y <= clientY && x + width > clientX && y + height > clientY
isMouseOver$.next(isMouseStillOver)
}}
aria-hidden
bind:this={wrapperElement}
>
<div
class="gradient"
style:--x={$gradientWiggle.at(0) + 'px'}
style:--y={$gradientWiggle.at(1) + 'px'}
style:--size={$gradientSize$ + 'px'}
></div>
<svg width="100%" height="100%" fill="none" xmlns="http://www.w3.org/2000/svg">
<pattern
id="background-pattern-id"
x="0"
y="0"
width="30"
height="30"
patternUnits="userSpaceOnUse"
>
<rect x="0.5" y="0.5" width="30" height="30" rx="0" stroke="currentColor" />
</pattern>
<rect
x="0"
y="0"
width="100%"
height="100%"
filter="url(#spotlight)"
fill="url(#background-pattern-id)"
>
</rect>
</svg>
</div>
<style lang="postcss">
.wrapper {
mask-image: linear-gradient(black 75%, transparent);
contain: strict;
user-select: none;
}
svg {
background: theme(colors.black);
}
.gradient {
position: absolute;
top: 0;
left: 0;
mix-blend-mode: color-dodge;
height: var(--size);
width: var(--size);
background: radial-gradient(
closest-side,
theme(colors.cyan.300),
theme(colors.blue.950 / 100%) 30%,
transparent
);
opacity: 100%;
translate: var(--x) var(--y);
z-index: 20;
}
</style>

View file

@ -0,0 +1,56 @@
<script>
import clsx from 'clsx'
/** @type { 'md'|'lg'|'xl'}*/
export let size = 'md'
/** @type { 'primary'|'outline'|'fancyOutline' }*/
export let type = 'primary'
$: classes = clsx(
'animate rounded text-sm font-bold hover:scale-[1.03] active:scale-95',
'primary' == type && 'bg-slate-200 text-black',
'outline' == type && 'bg-transparent text-white outline outline-2 outline-slate-200',
'fancyOutline' == type && 'fancy',
'md' == size && 'min-w-[5.5rem] px-4 py-2.5',
'lg' == size && 'min-w-[5.5rem] px-6 py-3 ',
'xl' == size && 'min-w-[5.5rem] px-8 py-4 ',
$$restProps.class
)
</script>
{#if type === 'fancyOutline'}
<div class="relative">
<button class={classes} on:click><slot>NO LABEL PROVIDED</slot></button>
<span
class="fancy-bg absolute inset-0 -z-10 h-full w-[110%] min-w-[5rem] scale-y-75 bg-cyan-500/90 px-4 py-2 blur-xl"
style="--easing: x; --duration: 8s;"
/>
<span
class="fancy-bg absolute inset-0 -z-10 h-full w-[110%] min-w-[5rem] scale-y-75 bg-secondary/90 px-4 py-2 blur-xl"
style="--easing: y; --duration: 8s;"
/>
<span
class="fancy-bg absolute inset-0 -z-10 h-full w-[110%] min-w-[5rem] scale-y-75 bg-purple-500/90 px-4 py-2 blur-xl"
style="--easing: z;--duration: 8s;"
/>
</div>
{:else}
<button class={classes} on:click><slot>NO LABEL PROVIDED</slot></button>
{/if}
<style lang="postcss">
.animate {
animation: pop 380ms cubic-bezier(0.1, -0, 0.42, 1.8);
transition: transform 180ms cubic-bezier(0.1, -0, 0.42, 1.8);
}
.fancy {
background: theme(colors.black / 50%);
/* background-clip: padding-box; */
outline: 2px theme(colors.primary) solid;
}
.fancy-bg {
animation: var(--easing, 'x') var(--duration, 8s) infinite;
}
</style>

View file

@ -0,0 +1,163 @@
<script>
import clsx from 'clsx'
import { createEventDispatcher, getContext, onDestroy, onMount } from 'svelte'
import { spring } from 'svelte/motion'
import { contextId as ctxId } from '../../routes/CommunitySlice.svelte'
import { lerp } from '$lib/Helper.mjs'
import { inview } from 'svelte-inview'
/** @type {string} */
export let image
/** @type {string} */
export let containerClass = ''
/** @type {number} */
export let size
/** @type {[number, number]} */
export let coordinates
/** @type {string | undefined} */
export let quote = undefined
/** @type {symbol}*/
export let contextId = ctxId
export let isAnimating = true
/** @type {HTMLElement}*/
export let element = undefined
/** @type {HTMLImageElement}*/
export let imageWrapper
/** @type {HTMLImageElement}*/
export let imageElement
const { biggestSize, getSectionElement } = getContext(contextId)
const dispatch = createEventDispatcher()
const relativeSize = size / biggestSize
const delay = (biggestSize - size) * 5
const dragCoordinates = spring([0, 0], {
damping: lerp(0.2, 0.03, relativeSize),
stiffness: lerp(0.2, 0.01, relativeSize),
// stiffness: lerp(0.81, 0.9, relativeSize),
precision: 0.001
})
let hasEnteredView = false
let hasImageLoaded = false
/** @type {import('interactjs').default} */
let interactionjs
function onViewEnter() {
setTimeout(() => (hasEnteredView = true), 550)
// Only load the library if the element entered the view, to improve performance
import('interactjs').then(({ default: interact }) => {
interactionjs = interact(imageElement).draggable({
inertia: { resistance: lerp(5, 200, relativeSize) },
listeners: {
move({ dx, dy }) {
dragCoordinates.update(([x, y]) => {
x += dx
y += dy
return [x, y]
})
},
start(event) {
dispatch('dragStart', event)
},
end(event) {
dispatch('dragEnd', event)
}
},
modifiers: [
interact.modifiers.restrictRect({
restriction: getSectionElement,
endOnly: true
})
]
})
})
}
onMount(() => {
// Nesecarry as the load image event might not get fired when its already loaded ( for example after a page reload )
hasImageLoaded = hasImageLoaded || imageElement.complete
})
onDestroy(() => interactionjs?.off())
</script>
<div
class={clsx(
'absolute left-0 top-0 touch-none select-none transition-opacity ',
containerClass,
hasImageLoaded ? 'opacity-100' : 'opacity-0'
)}
style:translate={coordinates.map((xy) => xy + 'px').join(' ')}
style="width: {size}px; height: {size}px;--delay: {delay}ms;"
aria-hidden="true"
bind:this={element}
>
<div
class={clsx(
'group absolute inset-0 h-full w-full touch-none select-none',
isAnimating && 'opacity-0'
)}
style:translate={`calc( ${$dragCoordinates[0]}px ) ${$dragCoordinates[1]}px`}
use:inview={{ unobserveOnEnter: true, threshold: 0.2 }}
class:_animate={isAnimating && hasEnteredView}
on:inview_enter={onViewEnter}
>
<div class="" bind:this={imageWrapper}>
<img
class="group h-full w-full touch-none select-none rounded-[50%] object-cover outline outline-4 {$$restProps.class}"
bind:this={imageElement}
on:load={() => (hasImageLoaded = true)}
src={image}
alt="community profile picture"
aria-hidden="true"
on:mouseenter={(event) => dispatch('hover', event)}
class:hover:scale-125={!!quote}
loading="lazy"
/>
<slot />
</div>
{#if quote}
<div class="quote" aria-hidden="true">
{quote}
</div>
{/if}
</div>
</div>
<style lang="postcss">
._animate {
animation: reveal 440ms 1 var(--delay) both cubic-bezier(0, 1, 0.765, 3.8);
touch-action: none;
user-select: none;
img {
transition: scale cubic-bezier(0.95, 0.82, 0.165, 2) 180ms;
&:hover {
scale: 1.05;
}
}
}
.quote {
@apply pointer-events-none absolute -top-6 left-1/2 min-w-max -translate-x-1/2 select-none rounded bg-slate-800/50 px-2 py-1 text-sm font-medium tracking-wide opacity-0 duration-150 group-hover:opacity-100;
}
@keyframes reveal {
from {
opacity: 0%;
scale: 0.72 0.72;
}
to {
opacity: 100%;
scale: 1 1;
}
}
</style>

114
src/lib/components/Footer.svelte Executable file
View file

@ -0,0 +1,114 @@
<script>
import DiscordIcon from '~icons/prime/discord'
import GithubIcon from '~icons/ri/github-fill'
import { discordLink } from '$lib/constants.mjs'
/** @type {[string, string, string, string]} */
let team = [
['Fufexan', 'Supporting Developer', 'cyan', 'https://github.com/fufexan'],
['NotAShelf', 'Real Chad', 'teal', 'https://github.com/NotAShelf'],
['VDawg', 'Webdesign-and dev', 'emerald', 'https://github.com/Visual-Dawg'],
['System-x64', 'Webdev', 'green', 'https://github.com/System-x64']
]
function createRole(role, color) {
return `<span class='text-${color}-500'><span class='text-${color}-600'>[ </span>${role}<span class='text-${color}-600'> ]</span></span>`
}
</script>
<footer
class="max-w-screen relative mt-16 flex items-center justify-center border-t border-blue-400/50 bg-black/50 md:mt-24 lg:mt-32"
>
<div class="footer-inner">
<div class="flex flex-col gap-4 rounded-lg">
<div class="pretitle">Humans</div>
<ul class="flex flex-col gap-3 font-medium">
<li>
<a href="https://github.com/vaxerski" target="_blank">
Vaxerski <span
class="bg-gradient-to-r from-primary to-blue-500 bg-clip-text text-transparent"
>[ Lead Developer ]</span
>
</a>
</li>
{#each team as [name, role, color, href]}
<li>
<a {href} target="_blank">{name} {@html createRole(role, color)}</a>
</li>
{/each}
<li>
<a href="https://github.com/hyprwm/Hyprland/graphs/contributors" target="_blank"
>and our <span class="text-indigo-500">fellow contributors</span></a
>
</li>
</ul>
</div>
<div class="flex flex-col gap-4">
<div class="pretitle">Links</div>
<ul class="flex flex-col gap-3 font-medium">
<li><a href="https://wiki.hyprland.org/" target="_blank">Wiki</a></li>
<li>
<a href="https://wiki.hyprland.org/Getting-Started/Master-Tutorial/" target="_blank"
>Get started</a
>
</li>
<li><a href="/hall_of_fame">Hall of fame</a></li>
</ul>
</div>
<div class="flex flex-col gap-4">
<div class="pretitle" font-bold>Socials</div>
<ul class="flex gap-6">
<li class="">
<a
href={discordLink}
class="text-slate-400 hover:text-slate-200"
aria-label="Join us on Discord"
target="_blank"><DiscordIcon class="h-12 w-12 " /></a
>
</li>
<li class="">
<a
href="https://github.com/hyprwm/Hyprland"
class="text-slate-400 hover:text-slate-200"
target="_blank"
aria-label="Go to our Github"><GithubIcon class="h-12 w-12 " /></a
>
</li>
</ul>
</div>
<div class="flex w-full flex-wrap gap-4 text-sm font-medium text-slate-400">
<div>Hyprland is licensed under the BSD 3-Clause "New" or "Revised" License.</div>
<div>© Hyprland Development {new Date().getFullYear()}.</div>
<div>Doki doki waku waku.</div>
</div>
</div>
<div class="gradient" aria-hidden="true" />
</footer>
<style lang="postcss">
.footer-inner {
@apply flex max-w-5xl flex-wrap items-start justify-between gap-12 px-8 py-14 text-slate-300;
}
.pretitle {
@apply text-sm font-bold uppercase text-slate-400;
}
a:hover {
filter: brightness(1.5);
}
.gradient {
position: absolute;
bottom: 50px;
left: 0px;
width: 100%;
height: 900px;
z-index: -10;
mask-image: radial-gradient(105vw 450px at 50% 50%, rgba(0, 0, 0, 1) 80%, transparent);
background: url('/imgs/grain.webp'),
radial-gradient(105vw 450px at 50% 50%, theme(colors.blue.600 / 80%), transparent);
}
</style>

View file

@ -0,0 +1,65 @@
<script>
import { lerp } from '$lib/Helper.mjs'
import { createNoise2D } from 'simplex-noise'
import { onMount } from 'svelte'
import { expoIn } from 'svelte/easing'
/** Lifespan in milliseconds */
export let lifeSpan = 1500
export let maxSpeed = 20
export let minSpeed = 4
export let maxOpacity = 1
export let scale = 1
const isDescending = Math.random() > 0.8
const wobbliness = lerp(0.0001, 0.004, Math.random())
const speed = Math.random() * (maxSpeed - minSpeed) + minSpeed
let x = Math.random() * 30 - 30
let y = Math.random() * 2
let lifeRemaining = lifeSpan
$: lifePercentage = lifeRemaining / lifeSpan
let timestamp = Date.now()
const noiseY = createNoise2D()
const noiseX = createNoise2D()
const colors = ['#0e4429', '#006d32', '#26a641', '#39d353']
const color = colors.at(Math.ceil(colors.length * Math.random()))
onMount(() => {
let animationId
let i = 0
animate()
async function animate() {
const deltaTime = (timestamp - Date.now()) / 17 // One frame should last roughly 17ms for 60fps
x += noiseX(i, 1) * speed * deltaTime * expoIn(lifePercentage)
y += noiseY(i, 1) * speed * deltaTime * expoIn(lifePercentage)
i += wobbliness * deltaTime
lifeRemaining -= Date.now() - timestamp
timestamp = Date.now()
if (lifeRemaining <= -1) return
animationId = requestAnimationFrame(animate)
}
return () => cancelAnimationFrame(animationId)
})
</script>
<div
class="absolute h-6 w-6 rounded-md"
style:translate={`${x}px ${y}px`}
style:background={color}
style:opacity={(lifeRemaining / lifeSpan - (1 - maxOpacity)) ** 5}
style:scale={isDescending ? (lifeRemaining / lifeSpan - (1 - scale)) ** 2 : undefined}
style:top={50 + (Math.random() * 5 - 5) + '%'}
style:left={50 + (Math.random() * 5 - 5) + '%'}
></div>

22
src/lib/components/Title.svelte Executable file
View file

@ -0,0 +1,22 @@
<script>
import { animateIn } from '$lib/Helper.mjs'
</script>
<div
use:animateIn={{ slide: 24, fade: 0 }}
class="z-10 flex flex-col items-center px-3 text-center {$$restProps.class}"
>
<div class="mb-3 font-extrabold text-slate-300">
<slot name="pre" />
</div>
<h1 class="mb-12 text-center text-5xl font-bold md:text-8xl">
<slot name="title">No title given!!!</slot>
</h1>
<div class="-mt-4 mb-7 text-center font-extrabold text-slate-300 sm:text-lg">
<slot name="subtitle" />
</div>
<slot />
</div>

View file

@ -0,0 +1,125 @@
<script>
import clsx from 'clsx'
import PlayIcon from '~icons/mingcute/play-circle-line'
import { inview } from 'svelte-inview'
import IconFullscreen from '~icons/mingcute/fullscreen-fill'
import IconPause from '~icons/mingcute/pause-circle-line'
import { onMount } from 'svelte'
/** @type {string[]} */
export let sources
/** @type {string} */
export let poster
export let loop = true
export let muted = true
/** @type {true | undefined}*/
export let autoplay = undefined
export let hidden = false
/** @type {string}*/
export let videoClass = ''
/** @type {string}*/
export let playButtonClass = ''
/** @type {HTMLVideoElement}*/
export let videoElement
let isPaused = true
function togglePlay() {
videoElement.paused ? videoElement.play() : videoElement.pause()
isPaused = videoElement.paused
}
function makeFullscreen() {
videoElement.requestFullscreen()
}
// Firefox does not seem to fire the play event when the video is autoplayed. So lets check manually if autoplaying worked
onMount(() => {
const timeout = setTimeout(() => {
isPaused = videoElement.paused
}, 5)
return () => clearTimeout(timeout)
})
</script>
<div
class="wrapper group {$$props.class}"
role="banner"
use:inview={poster}
on:inview_enter
on:inview_leave
{hidden}
>
<video
bind:this={videoElement}
{muted}
disablepictureinpicture="true"
disableremoteplayback="true"
class="rounded-xl {videoClass}"
{loop}
preload="auto"
{poster}
on:click={togglePlay}
on:dblclick={makeFullscreen}
on:play
on:pause
on:pause={() => (isPaused = true)}
{autoplay}
on:play={() => (isPaused = false)}
>
{#each sources as src}
<source {src} />
{/each}
</video>
<div
class="absolute bottom-2 left-2 flex gap-4 opacity-80 hover:opacity-100 group-hover:opacity-90"
>
{#if !isPaused}
<button on:click={() => videoElement.pause()} class="z-10">
<IconPause
class="h-6 w-6 rounded drop-shadow transition-all duration-75 hover:scale-105 "
/>
</button>
{:else}
<button on:click={() => videoElement.play()} class="z-10">
<PlayIcon
class="h-6 w-6 rounded drop-shadow transition-all duration-75 hover:scale-105 "
/>
</button>
{/if}
<button on:click={makeFullscreen} class="z-10">
<IconFullscreen
class="h-6 w-6 rounded drop-shadow transition-all duration-75 hover:scale-105 "
/>
</button>
</div>
<div
class={clsx(
'z-20 opacity-0 transition-opacity ',
isPaused ? 'opacity-100' : 'pointer-events-none'
)}
>
{#if isPaused}
<div
class="pointer-events-none absolute left-1/2 top-1/2 h-14 w-14 -translate-x-1/2 -translate-y-1/2 rounded-full opacity-80 hover:opacity-100 {playButtonClass}"
>
<PlayIcon class="h-full w-full" />
</div>
{/if}
</div>
</div>
<style lang="postcss">
.wrapper {
position: relative;
background: theme(colors.black);
}
video {
display: block;
width: 100%;
height: 100%;
}
</style>

1
src/lib/constants.mjs Normal file
View file

@ -0,0 +1 @@
export const discordLink = 'https://discord.com/invite/hQ9XvMUjjr'

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.3 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 3 KiB

BIN
src/lib/images/community-bg.png Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 MiB

BIN
src/lib/images/community-bg.webp Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 263 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 198 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 67 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 211 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 69 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 183 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 191 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 48 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 190 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 82 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 200 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 73 KiB

View file

@ -0,0 +1,29 @@
<svg
xmlns="http://www.w3.org/2000/svg"
xmlns:xlink="http://www.w3.org/1999/xlink"
viewBox="0 0 515.34 673.26"
class={$$props.class}
>
<defs>
<style>
.cls-3 {
fill: url(#linear-gradient);
}
</style>
<linearGradient
id="linear-gradient"
x1="8.93"
y1="572.38"
x2="572.68"
y2="102.08"
gradientUnits="userSpaceOnUse"
>
<stop offset="0" stop-color="#00a2f8" />
<stop offset="1" stop-color="#00e5cf" />
</linearGradient>
</defs>
<path
class="cls-3"
d="m288.53,0c1.99,2.11,3.26,3.13,4.14,4.42,15.97,23.63,31.7,47.44,47.91,70.91,9.33,13.51,19.26,26.61,29.22,39.67,9.63,12.65,19.8,24.88,29.44,37.53,11.15,14.63,22.45,29.18,32.81,44.36,13.27,19.45,26.64,38.94,38.12,59.44,9.76,17.43,17.56,36.05,25.02,54.64,5.28,13.16,8.82,27.06,12.6,40.78,1.75,6.36,2.24,13.07,3.26,19.62,1.26,8.02,3.4,16.04,3.53,24.08.35,20.94,2.17,41.95-1.48,62.8-4.02,22.99-10.75,45-20.76,66.23-10.12,21.45-22.38,41.26-37.39,59.59-13.6,16.61-29.57,30.33-46.84,42.91-12.68,9.23-26.09,17.07-40.36,23.3-12.84,5.6-26.07,10.09-39.69,13.89-30.75,8.56-61.93,10.44-93.33,8.23-18.93-1.33-38.08-3.65-56.11-10.46-14.82-5.6-29.75-11.13-43.92-18.13-11.19-5.52-21.91-12.44-31.73-20.15-12.88-10.11-25.52-20.81-36.67-32.74-9.99-10.68-18.64-22.87-26.4-35.31-8.18-13.13-15.39-27.01-21.58-41.2-6.69-15.32-11.01-31.45-14.53-47.97-5.48-25.71-3.71-51.44-2.59-77.09.64-14.73,4.53-29.47,8.26-43.86,3.93-15.18,8.68-30.26,14.44-44.84,5.77-14.6,12.47-28.96,20.14-42.65,9.63-17.18,20.25-33.85,31.2-50.23,9.94-14.88,20.68-29.24,31.47-43.52,8.69-11.5,18.17-22.41,26.97-33.83,8.9-11.55,17.46-23.36,26.07-35.13,9.11-12.46,18.29-24.87,27.09-37.55,11.16-16.07,21.91-32.43,32.97-48.57,1.58-2.31,3.88-4.14,5.84-6.19.39.22.78.44,1.18.66.08,1.77.23,3.54.23,5.31.01,26.33.15,52.66-.17,78.99-.05,3.86-1.62,8.15-3.72,11.46-8.5,13.45-17.19,26.8-26.38,39.79-8.71,12.31-18.12,24.13-27.22,36.16-7.5,9.91-14.97,19.83-22.52,29.7-5.24,6.85-10.74,13.5-15.86,20.44-7.16,9.72-14.35,19.45-21.03,29.5-8.06,12.12-15.99,24.36-23.23,36.97-5.18,9.02-9.26,18.69-13.59,28.17-2.4,5.26-4.61,10.64-6.36,16.14-3.1,9.76-5.58,19.71-8.68,29.47-8.72,27.42-6.87,55.63-4.92,83.5.99,14.15,6.11,28.15,10.4,41.89,6.01,19.24,16.32,36.3,27.95,52.74,7.94,11.23,16.95,21.38,27.14,30.36,8.39,7.38,17.5,14.17,27.07,19.92,10.89,6.54,22.23,12.77,34.12,17.07,12.69,4.59,26.1,7.99,39.48,9.68,15.93,2.01,32.16,1.58,48.25,2.34,14.94.7,29.43-2.29,43.93-5.14,18.41-3.62,35.23-11.56,51.58-20.26,19.55-10.4,35.98-25.13,50.37-41.73,14.71-16.97,27.06-36.05,34.92-57,8.29-22.1,15.2-44.97,14.15-69.26-.57-13.13.15-26.34-1.06-39.39-.89-9.61-3.62-19.11-6.16-28.5-2.98-11.03-6.03-22.1-10.16-32.73-4.11-10.59-9.36-20.75-14.52-30.9-4.57-8.99-9.32-17.92-14.66-26.46-6.5-10.39-13.38-20.59-20.68-30.43-11.05-14.9-22.74-29.32-33.91-44.12-13.08-17.33-26.13-34.68-38.77-52.33-10.91-15.22-21.31-30.81-31.71-46.39-1.67-2.5-3-5.79-3.03-8.72-.23-28.49-.15-56.98-.13-85.47,0-.95.24-1.91.58-4.44Z"
/>
</svg>

After

Width:  |  Height:  |  Size: 2.8 KiB

10
src/lib/images/logos/arch.svg Executable file
View file

@ -0,0 +1,10 @@
<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M35.6255 9C33.2545 14.8302 31.8244 18.6438 29.1845 24.3007C30.8031 26.0214 32.7898 28.0252 36.0162 30.2883C32.5475 28.8568 30.1814 27.4196 28.4132 25.9282C25.0347 32.9986 19.7415 43.0701 9 62.4268C17.4425 57.5386 23.9869 54.5249 30.086 53.375C29.8241 52.2453 29.6752 51.0233 29.6854 49.7482L29.6954 49.477C29.8293 44.0523 32.643 39.8807 35.9761 40.164C39.3092 40.4472 41.9 45.077 41.766 50.5017C41.7408 51.5225 41.626 52.5044 41.4254 53.4152C47.4583 54.5988 53.9328 57.6047 62.261 62.4268C60.6189 59.3946 59.1531 56.6613 57.7533 54.0582C55.5485 52.3443 53.2487 50.1136 48.5576 47.6988C51.782 48.5391 54.0906 49.5085 55.8902 50.5921C41.6584 24.0176 40.5059 20.4864 35.6255 9Z" fill="url(#paint0_linear_1413_1353)"/>
<defs>
<linearGradient id="paint0_linear_1413_1353" x1="18.6377" y1="2.77914" x2="61.3673" y2="3.35572" gradientUnits="userSpaceOnUse">
<stop stop-color="#00E6CF"/>
<stop offset="0.489583" stop-color="#00C4E3"/>
<stop offset="1" stop-color="#00A1F8"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 1.1 KiB

View file

@ -0,0 +1,22 @@
<svg width="74" height="74" viewBox="0 0 74 74" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M68.4738 6.54513C71.8755 9.94514 62.4451 24.8908 60.8513 26.4855C59.2571 28.0773 55.2084 26.6131 51.8076 23.2118C48.4063 19.8114 46.941 15.7619 48.5352 14.1677C50.129 12.5726 65.0734 3.14386 68.4738 6.54513Z" fill="url(#paint0_linear_1425_1638)"/>
<path d="M20.7735 10.1429C15.5802 7.19589 8.19153 3.91848 5.84065 6.26927C3.45844 8.65028 6.85634 16.2061 9.83373 21.4089C12.4832 16.8018 16.2529 12.924 20.7735 10.1429Z" fill="url(#paint1_linear_1425_1638)"/>
<path d="M63.2865 25.3123C63.7643 26.9347 63.6782 28.2747 62.9032 29.0484C61.0911 30.8608 56.198 28.9319 51.7874 24.7338C51.4788 24.4579 51.174 24.1712 50.8738 23.8705C49.2792 22.2751 48.0381 20.5759 47.2442 19.0119C45.6991 16.2409 45.3127 13.7931 46.4806 12.6255C47.1169 11.9894 48.1351 11.816 49.377 12.0398C50.1869 11.528 51.1425 10.957 52.1909 10.3725C47.9281 8.14937 43.0816 6.89316 37.9395 6.89316C20.887 6.89316 7.06152 20.7157 7.06152 37.7703C7.06152 54.8224 20.887 68.6467 37.9395 68.6467C54.9926 68.6467 68.8177 54.8224 68.8177 37.7703C68.8177 32.2631 67.3726 27.0985 64.8467 22.6215C64.3013 23.6174 63.7706 24.5301 63.2865 25.3123Z" fill="url(#paint2_linear_1425_1638)"/>
<defs>
<linearGradient id="paint0_linear_1425_1638" x1="51.7482" y1="29.6108" x2="68.8505" y2="29.3794" gradientUnits="userSpaceOnUse">
<stop stop-color="#00E6CF"/>
<stop offset="0.489583" stop-color="#00C4E3"/>
<stop offset="1" stop-color="#00A1F8"/>
</linearGradient>
<linearGradient id="paint1_linear_1425_1638" x1="7.88564" y1="23.2652" x2="20.5095" y2="23.0965" gradientUnits="userSpaceOnUse">
<stop stop-color="#00E6CF"/>
<stop offset="0.489583" stop-color="#00C4E3"/>
<stop offset="1" stop-color="#00A1F8"/>
</linearGradient>
<linearGradient id="paint2_linear_1425_1638" x1="18.2365" y1="75.8371" x2="67.7814" y2="75.1665" gradientUnits="userSpaceOnUse">
<stop stop-color="#00E6CF"/>
<stop offset="0.489583" stop-color="#00C4E3"/>
<stop offset="1" stop-color="#00A1F8"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 2 KiB

23
src/lib/images/logos/nixos.svg Executable file
View file

@ -0,0 +1,23 @@
<svg width="72" height="72" viewBox="0 0 72 72" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_1413_1526)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M22.3891 36.8152L39.4522 66.3727L31.6106 66.4463L27.0552 58.5053L22.4672 66.4038L18.571 66.4023L16.5755 62.9547L23.1119 51.7154L18.4719 43.6408L22.3891 36.8152Z" fill="#5277C3" fill-opacity="0.77"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M28.5478 24.6401L11.4818 54.196L7.49729 47.4418L12.0967 39.5262L2.96235 39.5022L1.01562 36.1272L3.00354 32.6752L16.0053 32.7163L20.6781 24.6606L28.5478 24.6401Z" fill="#7EBAE4" fill-opacity="0.77"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M29.8571 48.2666L63.9862 48.2683L60.1291 55.096L50.9743 55.0707L55.5207 62.9932L53.5712 66.3666L49.5878 66.3711L43.1224 55.0907L33.8096 55.0718L29.8571 48.2666Z" fill="#7EBAE4" fill-opacity="0.77"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M49.7214 35.3159L32.6583 5.75837L40.4998 5.68481L45.0553 13.6258L49.6432 5.72725L53.5394 5.72878L55.5349 9.17635L48.9985 20.4157L53.6385 28.4903L49.7214 35.3159Z" fill="#7EBAE4" fill-opacity="0.77"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M22.3891 36.8152L39.4522 66.3727L31.6106 66.4463L27.0552 58.5053L22.4672 66.4038L18.571 66.4023L16.5755 62.9547L23.1119 51.7154L18.4719 43.6408L22.3891 36.8152Z" fill="#5277C3" fill-opacity="0.77"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M42.2081 23.7897L8.07892 23.7881L11.936 16.9603L21.0908 16.9857L16.5444 9.06311L18.4939 5.6897L22.4773 5.6853L28.9427 16.9657L38.2555 16.9846L42.2081 23.7897Z" fill="#5277C3" fill-opacity="0.77"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M43.5395 47.4846L60.6055 17.9287L64.59 24.6829L59.9906 32.5986L69.1249 32.6226L71.0717 35.9975L69.0838 39.4495L56.082 39.4084L51.4092 47.4641L43.5395 47.4846Z" fill="#5277C3" fill-opacity="0.77"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M22.4093 36.7961L39.4724 66.3537L31.6309 66.4272L27.0754 58.4862L22.4875 66.3848L18.5913 66.3833L16.5958 62.9357L23.1322 51.6964L18.4922 43.6218L22.4093 36.7961Z" fill="#58E1FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M28.568 24.6209L11.502 54.1767L7.51752 47.4225L12.1169 39.5069L2.98258 39.4829L1.03581 36.1079L3.02373 32.656L16.0255 32.697L20.6983 24.6413L28.568 24.6209Z" fill="#58E1FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M29.8773 48.2475L64.0064 48.2492L60.1494 55.077L50.9945 55.0516L55.5409 62.9742L53.5915 66.3476L49.608 66.352L43.1427 55.0716L33.8299 55.0527L29.8773 48.2475Z" fill="#58E1FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M49.7417 35.2969L32.6786 5.73933L40.5201 5.66578L45.0756 13.6068L49.6635 5.70821L53.5597 5.70975L55.5552 9.15731L49.0188 20.3967L53.6588 28.4712L49.7417 35.2969Z" fill="#58E1FF"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M22.4093 36.7961L39.4724 66.3537L31.6309 66.4272L27.0754 58.4862L22.4875 66.3848L18.5913 66.3833L16.5958 62.9357L23.1322 51.6964L18.4922 43.6218L22.4093 36.7961Z" fill="#4DCAFF" fill-opacity="0.8"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M42.2032 23.7774L8.07407 23.7757L11.9311 16.9479L21.086 16.9733L16.5396 9.05072L18.489 5.67729L22.4725 5.6729L28.9378 16.9533L38.2506 16.9722L42.2032 23.7774Z" fill="#4DCAFF" fill-opacity="0.8"/>
<path fill-rule="evenodd" clip-rule="evenodd" d="M43.5442 47.4673L60.6102 17.9114L64.5947 24.6656L59.9953 32.5813L69.1296 32.6053L71.0764 35.9802L69.0885 39.4322L56.0867 39.3911L51.4139 47.4468L43.5442 47.4673Z" fill="#4DCAFF" fill-opacity="0.8"/>
</g>
<defs>
<clipPath id="clip0_1413_1526">
<rect width="72" height="72" fill="white"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 3.5 KiB

View file

@ -0,0 +1,10 @@
<svg width="74" height="74" viewBox="0 0 74 74" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M57.2005 29.684C57.2357 28.7296 57.6335 27.8438 58.3279 27.1941C59.0227 26.5422 59.9294 26.2054 60.8802 26.2357C62.8403 26.3061 64.3804 27.9687 64.3141 29.9363C64.2771 30.8907 63.8795 31.7765 63.1867 32.4242C62.4919 33.0799 61.5875 33.4169 60.6324 33.3846C58.6747 33.3122 57.1363 31.6534 57.2025 29.684H57.2005ZM66.709 33.203C66.8706 33.0957 66.9893 33.0069 67.0393 32.9871L67.0933 32.9026C66.9566 32.039 65.6756 27.8521 64.7077 26.8452C64.4408 26.5708 64.2269 26.3004 63.7942 26.046C60.288 23.9939 51.9744 22.755 51.5848 22.6985L51.5185 22.7169L51.4936 22.7774C51.4936 22.7774 51.4615 24.5087 51.4567 24.7022C50.6075 24.4154 44.4343 22.412 38.6469 22.2102C33.739 22.0369 26.5921 21.3991 17.1198 27.2506L16.8397 27.4258C12.3837 30.2183 9.30722 33.6627 7.69872 37.6639C7.19428 38.923 6.51593 41.764 7.18825 44.4376C7.47782 45.6061 8.0178 46.7802 8.74575 47.8314C10.3912 50.2064 13.1516 51.7822 16.1256 52.0486C20.3226 52.4259 23.5014 50.5314 24.632 46.984C25.4093 44.5364 24.632 40.9448 21.6519 39.1127C19.2281 37.6213 16.6216 37.9603 15.1095 38.9654C13.7978 39.8393 13.0552 41.197 13.0672 42.6882C13.0976 45.3335 15.3645 46.7398 16.9951 46.7439C17.4688 46.7439 17.9446 46.6611 18.4809 46.4856C18.6719 46.4271 18.8514 46.3546 19.0494 46.2316L19.1115 46.195L19.1504 46.1688L19.1374 46.1767C19.5091 45.9222 19.7326 45.5084 19.7326 45.0647C19.7326 44.9436 19.7157 44.8209 19.6812 44.6974C19.4886 44.0275 18.8299 43.6222 18.1474 43.7471L18.0552 43.7691L17.9305 43.8074L17.7496 43.87C17.374 43.9628 17.0929 43.9709 17.0329 43.9729C16.8419 43.9606 15.9033 43.6781 15.9033 42.6452V42.6311C15.9033 42.2517 16.0547 41.9856 16.1382 41.8401C16.4302 41.38 17.2297 40.9285 18.309 41.0229C19.7266 41.1462 20.7489 41.8766 21.4256 43.2545C22.054 44.5356 21.8895 46.1117 21.0019 47.2659C20.1204 48.4097 18.552 48.8958 16.4636 48.67C14.3591 48.4362 12.5799 47.2193 11.5838 45.3246C10.6087 43.4727 10.5555 41.2769 11.4463 39.5901C13.5769 35.5485 17.6012 35.5909 19.8081 35.9742C23.0733 36.5434 26.7883 39.5699 28.1057 43.0647C28.3185 43.6218 28.427 44.0633 28.5217 44.4672L28.6639 45.0705L32.3528 46.8803L32.401 46.9168L32.4906 46.9085L32.5069 46.8018C32.484 46.7189 32.4303 46.6426 32.3452 45.6052C32.2751 44.6847 32.1323 42.1649 33.395 40.9159C33.8852 40.4279 34.6322 39.9922 35.2222 39.8528C37.64 39.2594 40.4755 39.6688 43.1563 42.7866C44.5441 44.397 45.2207 45.1313 45.56 45.4602L45.6785 45.5675L45.8179 45.6722C45.9291 45.733 50.4285 47.8151 50.4285 47.8151L50.5211 47.7929L50.5229 47.6963C50.493 47.662 47.6714 43.9977 48.1734 40.9811C48.5688 38.578 50.4727 38.7959 53.1054 39.0945C53.9648 39.1954 54.9432 39.3084 55.9569 39.331C58.7843 39.349 61.8307 38.8243 63.7082 37.9991C64.923 37.4684 65.6981 37.1153 66.1843 36.6716C66.359 36.5261 66.4493 36.29 66.5439 36.036L66.61 35.8683C66.6885 35.6623 66.8042 35.2285 66.8548 34.9906C66.8753 34.8858 66.8873 34.7751 66.8128 34.7164L66.553 34.7648C65.7284 35.2634 63.6673 36.2075 61.7415 36.2478C59.3498 36.2965 54.5323 33.8306 54.0303 33.5682L53.9819 33.5119C53.8636 33.2214 53.1405 31.5045 52.9859 31.1411C56.4539 33.4373 59.3295 34.7065 61.5325 34.9022C63.9844 35.1215 65.8941 33.7775 66.7114 33.2028H66.7112L66.709 33.203ZM61.2569 30.015C60.6483 30.015 60.1567 29.684 60.1567 29.2785C60.1567 28.8691 60.6483 28.54 61.2569 28.54C61.8656 28.54 62.3586 28.8691 62.3586 29.2785C62.3582 29.684 61.8652 30.015 61.2569 30.015ZM60.8511 27.2889C60.1862 27.2649 59.5559 27.501 59.0739 27.955C58.5922 28.409 58.308 29.024 58.2879 29.6901C58.2417 31.0664 59.3143 32.2244 60.6816 32.2749C61.3461 32.295 61.9797 32.059 62.4644 31.605C62.9482 31.1487 63.2265 30.5333 63.2476 29.8699C63.2938 28.494 62.2216 27.3376 60.8519 27.2891L60.8511 27.2889Z" fill="url(#paint0_linear_1427_1838)"/>
<defs>
<linearGradient id="paint0_linear_1427_1838" x1="17.7976" y1="55.5884" x2="66.0566" y2="54.2777" gradientUnits="userSpaceOnUse">
<stop stop-color="#00E6CF"/>
<stop offset="0.489583" stop-color="#00C4E3"/>
<stop offset="1" stop-color="#00A1F8"/>
</linearGradient>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 4 KiB

BIN
src/lib/images/vaxry-github.webp Executable file

Binary file not shown.

After

Width:  |  Height:  |  Size: 27 KiB

17
src/routes/+layout.svelte Executable file
View file

@ -0,0 +1,17 @@
<script>
import Footer from '$lib/components/Footer.svelte'
import { onMount } from 'svelte'
import Navbar from './Navbar.svelte'
import './styles.css'
import '@fontsource-variable/work-sans'
import '@fontsource/ibm-plex-mono/500.css'
import { getRandom } from '$lib/Helper.mjs'
</script>
<Navbar />
<main class="mx-auto flex w-full flex-col overflow-hidden">
<slot />
</main>
<Footer />

4
src/routes/+page.js Executable file
View file

@ -0,0 +1,4 @@
// since there's no dynamic data here, we can prerender
// it so that it gets served as a static asset in production
export const prerender = true
export const trailingSlash = 'always'

71
src/routes/+page.server.js Executable file
View file

@ -0,0 +1,71 @@
// since there's no dynamic data here, we can prerender
// it so that it gets served as a static asset in production
import baseColors from 'tailwindcss/colors'
export const load = () => ({
backgroundData: getHeroBackgroundTiles()
})
function getHeroBackgroundTiles() {
const workspacesPerRow = 4
const workspaceHeight = 240
const gapLength = 32
const colors = [baseColors.blue[500], baseColors.cyan[400], baseColors.sky[500]]
const images = [
'/imgs/chan/joy.svg',
'/imgs/chan/surprise.svg',
'/imgs/chan/tongueout.svg',
'/imgs/waylnad.webp'
]
const leftColumns = Array.from({ length: 3 }, () => generateRow(workspacesPerRow))
const rightColumns = Array.from({ length: 3 }, () => generateRow(workspacesPerRow))
/** Used to transform the rows by their own lenght*/
const height = workspacesPerRow * (workspaceHeight + gapLength)
return {
leftColumns,
rightColumns,
height,
workspacesPerRow,
workspaceHeight,
gapLength
}
function generateRow(amount) {
return Array.from({ length: amount }).map(generateWorkspace)
// If the background should be animated
// return [...base, ...base]
}
function generateWorkspace() {
return [
generateTiles(),
Math.random() > 0.4 ? generateTiles() : undefined,
Math.random() > 0.7 ? generateTiles() : undefined
].filter(Boolean)
}
function generateTiles() {
const result = Math.random() > 0.5 ? [generateTile()] : [generateTile(), generateTile()]
return result
}
function generateTile() {
return { color: getRandomColor(), image: Math.random() > 0.7 ? getRandomImage() : undefined }
}
/** @returns {string} */
function getRandomColor() {
return colors.at(Math.floor(Math.random() * colors.length))
}
/** @returns {string} */
function getRandomImage() {
return images.at(Math.floor(Math.random() * images.length))
}
}

40
src/routes/+page.svelte Executable file
View file

@ -0,0 +1,40 @@
<script>
import Community from './CommunitySlice.svelte'
import FeaturesSlice from './FeaturesSlice.svelte'
import HallOfFameSlice from './HallOfFameSlice.svelte'
import Hero from './Hero.svelte'
import InstallSlice from './InstallSlice.svelte'
import PreviewRiceSlice from './PreviewRiceSlice.svelte'
import PluginsSlice from './PluginsSlice.svelte'
export let data
</script>
<Hero backgroundData={data.backgroundData} />
<div class="-mt-8 flex flex-col items-center gap-20 md:gap-[16rem]">
<PreviewRiceSlice class="mb-12" />
<FeaturesSlice />
<PluginsSlice />
<HallOfFameSlice />
<Community />
<InstallSlice />
</div>
<svelte:head>
<title>Hyprland</title>
<meta name="description" content="Hyprland - Dynamic tiling Wayland compositor with the looks." />
<meta
property="og:description"
content="Hyprland - Dynamic tiling Wayland compositor with the looks."
/>
<meta property="og:title" content="Hyprland: Dynamic tiling window compositor with the looks" />
</svelte:head>
<style>
</style>

282
src/routes/CommunitySlice.svelte Executable file
View file

@ -0,0 +1,282 @@
<script context="module">
export const contextId = Symbol('community context')
</script>
<script>
import Button from '$lib/components/Button.svelte'
import DiscordIcon from '~icons/prime/discord'
import DiscordProfilePicture from '$lib/components/DiscordProfilePicture.svelte'
import { setContext } from 'svelte'
import Title from '$lib/components/Title.svelte'
import background from '$lib/images/community-bg.webp'
import amongUsGreenImage from '$lib/images/amongus/green.webp'
import { discordLink } from '$lib/constants.mjs'
let sectionElement
let isDraggingChan = false
/** @type {{image: string coordinates: [number, number] containerClass: string}[], size: number, quote?: string } */
const profiles = [
{
image: '/imgs/profile_pictures/vaxry.webp',
coordinates: [187, 296],
size: 172,
class: 'outline-red-500'
},
{
image: '/imgs/profile_pictures/fufexan.webp',
coordinates: [735, 441],
size: 164,
class: 'outline-yellow-500'
},
{
image: '/imgs/profile_pictures/raf-notashelf.webp',
coordinates: [391, 615],
size: 149,
class: 'outline-orange-500'
},
{
image: '/imgs/profile_pictures/Mathisbuilder.webp',
coordinates: [568, 594],
size: 120,
class: 'outline-amber-500'
},
{
image: '/imgs/profile_pictures/dani_666..webp',
coordinates: [525, 764],
size: 80,
class: 'outline-red-500'
},
{
image: '/imgs/profile_pictures/end_4.webp',
coordinates: [648, 709],
size: 128,
class: 'outline-cyan-400'
},
{
image: '/imgs/profile_pictures/ardishco.webp',
coordinates: [65, 208],
size: 100,
class: 'outline-slate-200'
},
{
image: '/imgs/profile_pictures/kcrmson.webp',
coordinates: [53, 399],
size: 75,
class: 'outline-sky-500'
},
{
image: '/imgs/profile_pictures/loseardes77.webp',
coordinates: [24, 341],
size: 49,
class: 'outline-green-500'
},
{
image: '/imgs/profile_pictures/captainiveau.webp',
coordinates: [47, 86],
size: 48,
class: 'outline-red-500'
},
{
image: '/imgs/profile_pictures/etrigan63.webp',
coordinates: [824, 738],
size: 58,
class: 'outline-amber-500'
},
{
image: '/imgs/profile_pictures/jsw.webp',
coordinates: [41, 566],
size: 49,
class: 'outline-sky-500'
},
{
image: 'imgs/chan/joy.svg',
coordinates: [284, 533],
size: 90,
class: 'outline-cyan-500 bg-blue-300',
onDragStart: ({ detail: { currentTarget } }) => {
isDraggingChan = true
currentTarget.src = 'imgs/chan/surprise.svg'
},
onDragEnd: ({ detail: { currentTarget } }) => {
isDraggingChan = false
currentTarget.src = 'imgs/chan/tongueout.svg'
},
onHover: ({ detail: { srcElement } }) =>
!isDraggingChan && (srcElement.src = 'imgs/chan/wink.svg')
},
{
image: '/imgs/profile_pictures/7.webp',
coordinates: [273, 760],
size: 52,
quote: '"meds"',
class: 'outline-cyan-500'
},
{
image: '/imgs/profile_pictures/outfoxxed.webp',
coordinates: [648, 364],
size: 80,
class: 'outline-orange-500'
},
{
image: '/imgs/profile_pictures/kirottum.webp',
coordinates: [772, 651],
size: 62,
class: 'outline-purple-500'
},
{
image: '/imgs/profile_pictures/beardwarrior.webp',
coordinates: [736, 277],
size: 87,
class: 'outline-amber-500'
},
{
image: '/imgs/profile_pictures/neoney.webp',
coordinates: [898, 364],
size: 68,
class: 'outline-green-500'
},
{
image: amongUsGreenImage,
coordinates: [873, 224],
size: 79,
class: 'outline-green-500'
},
{
image: '/imgs/profile_pictures/SimplyKyle!.webp',
coordinates: [859, 159],
size: 39,
class: 'outline-rose-500'
},
{
image: '/imgs/profile_pictures/sioodmy.webp',
coordinates: [974, 107],
size: 48,
class: 'outline-amber-500'
},
{
image: '/imgs/profile_pictures/flafy.webp',
coordinates: [147, 553],
size: 87,
class: 'outline-pink-500'
},
{
image: '/imgs/profile_pictures/vagahbond.webp',
coordinates: [65, 643],
size: 74,
class: 'outline-amber-500 '
},
{
image: '/imgs/profile_pictures/flick0.webp',
coordinates: [263, 653],
size: 65,
class: 'outline-stone-500 '
},
{
image: '/imgs/profile_pictures/jacekpoz.svg',
coordinates: [893, 622],
size: 80,
class: 'outline-yellow-500 bg-black ',
quote: '"piss blob"'
},
{
image: '/imgs/profile_pictures/aylur.webp',
coordinates: [354, 798],
size: 80,
class: 'outline-amber-500 bg-black '
},
{
image: '/imgs/profile_pictures/aleph.nought.webp',
coordinates: [583, 824],
size: 40,
class: 'outline-blue-500 bg-black '
}
].sort(({ size: a }, { size: b }) => b - a)
setContext(contextId, {
biggestSize: profiles.reduce(
(previousSize, { size }) => (size > previousSize ? size : previousSize),
1
),
smallestSize: profiles.reduce(
(previousSize, { size }) => (size < previousSize ? size : previousSize),
Number.POSITIVE_INFINITY
),
getSectionElement: () => sectionElement
})
</script>
<section
class="relative -mb-[200px] flex h-[1100px] min-h-max w-screen flex-col items-center"
bind:this={sectionElement}
>
<Title>
<span slot="title">Join a great<br />community</span>
<span slot="subtitle">
Get help from Distro Hoppers, Haiku writers,<br />Hydrohomies and human_(probably)
</span>
</Title>
<div class="group mt-16 flex flex-col items-center">
<a
class="discord p-4"
href={discordLink}
target="_blank"
rel="noopener"
aria-label="Join us on Discord"
>
<DiscordIcon class="h-full w-full " />
</a>
<a href={discordLink}>
<Button type="fancyOutline">Join us on Discord</Button>
</a>
</div>
<div class="absolute w-[1024px] select-none">
<div class="flex h-full origin-bottom-right select-none flex-wrap gap-4">
{#each profiles as { onDragEnd, onDragStart, onHover, ...props }}
<DiscordProfilePicture
{...props}
on:dragStart={onDragStart}
on:dragEnd={onDragEnd}
on:hover={onHover}
/>
{/each}
</div>
</div>
<img
src={background}
class="absolute top-0 -z-10 min-w-[1400px] select-none"
alt=""
aria-hidden="true"
loading="lazy"
/>
</section>
<style lang="postcss">
.discord {
width: 9rem;
height: 9rem;
transition:
rotate 500ms cubic-bezier(0.5, 0, 0.5, 1),
scale 420ms cubic-bezier(0.5, 0.1, 0, 1),
filter 840ms;
transition-delay: 240ms, 180ms, 20ms;
transform: translateY(-25%);
filter: drop-shadow(0px 0px 0px cyan) drop-shadow(0px 0px 0px blue);
&:hover,
.group:hover & {
scale: 1.2 1.2;
rotate: 360deg;
filter: drop-shadow(4px 4px 14px #0fffef7a) drop-shadow(-4px -4px 12px purple);
animation: bounce 0.7s infinite 180ms both;
}
&:active {
scale: 1;
transition: scale 80ms;
}
}
</style>

View file

@ -0,0 +1,65 @@
<script>
import { animateIn, getBlurredPath } from '$lib/Helper.mjs'
/** @type {string}
* The path to the image. Usually the file within `static`, but can also be an URL
*/
export let image
/** @type {string | undefined} */
export let imageClass = undefined
/** @type {string | undefined} */
export let containerClass = undefined
/** @type {string}
* The path to the image. Usually the file within `static`, but can also be an URL. Defaults to `generated_<image>`
*/
export let blurredBackground = undefined
</script>
<div class="rice {containerClass} group">
<div class="h-full w-full" use:animateIn={{ slide: 20, duration: 800 }}>
<img
src={image}
alt="Rice desktop"
class="nice-hover w-full rounded-xl object-cover shadow-2xl hover:scale-[1.01] {imageClass}"
/>
<div class="rice-blurred">
<img
src={blurredBackground ?? getBlurredPath(image)}
alt="Rice desktop"
aria-hidden="true"
class="h-full w-full object-cover"
loading="lazy"
/>
</div>
</div>
</div>
<style lang="postcss">
.rice {
@apply relative h-auto w-full max-w-[1100px];
}
.nice-hover {
transition: all 540ms cubic-bezier(0.1, -0.81, 0.31, 2);
}
.rice-blurred {
translate: -50% 30%;
position: absolute;
bottom: -40px;
left: 50%;
pointer-events: none;
width: calc(100% + 120px);
height: calc(150% + 120px);
opacity: 0.9;
/* filter: brightness(2.5); */
z-index: -10;
mask-image: radial-gradient(50% 50% at 50% 50%, black, transparent);
contain: content layout size style;
@apply -z-10 transition-[filter] duration-500;
/* Too laggy on firefox */
/* .rice:hover & {
filter: brightness(4);
} */
}
</style>

213
src/routes/FeatureCard.svelte Executable file
View file

@ -0,0 +1,213 @@
<script>
import clsx from 'clsx'
import { getContext, onMount } from 'svelte'
import { mouseContext } from './FeaturesSlice.svelte'
import { spring } from 'svelte/motion'
import { getIsMobile } from '$lib/Helper.mjs'
export let title
export let color = 'cyan'
const { x: mouseX, y: mouseY, isHoverCards } = getContext(mouseContext)
/** @type HTMLDivElement */
let container
let isMobile = false
const damping = 0.2
const fillX = spring(999, { damping, stiffness: 0.021, precision: 0.3 })
const fillY = spring(999, { damping, stiffness: 0.021, precision: 0.3 })
const borderX = spring(999, { damping, stiffness: 0.03, precision: 0.3 })
const borderY = spring(999, { damping, stiffness: 0.03, precision: 0.3 })
const bounceBack = 2
const soft = 0.8
let isMouseOver = false
/** Has the mouse entered and not left*/
let hasMouseEntered = false
$: {
if (container && $mouseX !== undefined) {
updateGradient()
}
}
onMount(() => {
isMobile = getIsMobile()
})
function updateGradient() {
if (isMobile) return
const { x: rectX, y: rectY, width, height } = container.getBoundingClientRect()
const normX = $mouseX - rectX
const normY = $mouseY - rectY
$borderX = normX
$borderY = normY
if (!isMouseOver) {
hasMouseEntered = false
return
}
// Instantly update the blob positon without easing when the mouse has just entered
if (hasMouseEntered === false) {
fillX.set(normX, { hard: true })
fillY.set(normY, { hard: true })
hasMouseEntered = true
return
}
if ($mouseX < rectX) fillX.set(rectX + bounceBack, { soft })
else if ($mouseX > rectX + width) fillX.set(rectX + width - bounceBack, { soft })
else fillX.set(normX)
if ($mouseY < rectY) fillY.set(rectY + bounceBack, { soft: 1 })
if ($mouseY > rectY + height) fillX.set(rectY - height - bounceBack, { soft })
else fillY.set(normY)
}
</script>
<div
class={clsx('card group min-h-[20rem]', $$restProps.class)}
style:--x={$fillX}
style:--y={$fillY}
style:--borderX={$borderX}
style:--borderY={$borderY}
class:isHoverCards={$isHoverCards}
bind:this={container}
on:mouseenter={() => (isMouseOver = true)}
on:mouseleave={() => {
isMouseOver = false
updateGradient()
}}
class:purpleGradient={color === 'purple'}
role="contentinfo"
>
<div class="z-10 flex h-full w-full flex-col justify-end p-8 sm:p-12">
<h1 class="mb-6 text-5xl font-bold text-white">{title}</h1>
<slot>Nothing in the slot here</slot>
</div>
<div class="gradient max-sm:hidden" />
<div class="gradient_black max-sm:hidden" />
<div class="border-gradient max-sm:hidden" />
</div>
<style lang="postcss">
.card {
@apply relative flex h-full w-full items-end justify-end rounded-3xl transition-colors duration-300;
z-index: 2;
contain: paint style layout;
background: radial-gradient(
100% 100% at 100% 0%,
theme(colors.blue.950),
theme(colors.neutral.950)
);
/* The card border */
@media screen(md) {
background: theme(colors.slate.900);
&:hover {
background: theme(colors.blue.900);
}
}
}
.gradient_black {
@apply absolute inset-[2px];
z-index: 2;
border-radius: inherit;
contain: strict;
background: black
radial-gradient(
circle at bottom right,
theme(colors.neutral.900 / 80%),
theme(colors.neutral.500 / 10%),
black
);
}
/* This gradient is visible on the borders when hovering */
.border-gradient {
position: absolute;
z-index: 1;
border-radius: inherit;
width: 100%;
height: 100%;
opacity: 0%;
transform-origin: top left;
transition: opacity 120ms ease-in-out;
left: 0%;
top: 0%;
content: '';
pointer-events: none;
filter: brightness(1.5) saturate(4);
contain: strict;
background: radial-gradient(
620px circle at calc(var(--borderX) * 1px) calc(var(--borderY) * 1px),
color-mix(in srgb, var(--color1, theme(colors.cyan.500)), transparent 50%),
transparent
);
.isHoverCards & {
opacity: 100%;
}
}
.gradient {
@apply pointer-events-none absolute inset-0 h-full w-full;
width: calc(100% - 2px);
border-radius: inherit;
height: calc(100% - 2px);
border-radius: inherit;
margin: 0px;
z-index: 20;
mix-blend-mode: hard-light;
contain: strict;
/* Gradient blob */
&::before {
position: absolute;
z-index: 20;
border-radius: inherit;
min-width: 200%;
min-height: 200%;
aspect-ratio: 1 / 1;
scale: 0.5 0.5;
translate: -25% 0%;
transform-origin: top left;
left: 50%;
opacity: 0%;
transition: all 820ms;
content: '';
pointer-events: none;
background: radial-gradient(
ellipse at calc(var(--x) * 1px) calc(var(--y) * 1px),
var(--color1, theme(colors.cyan.500 / 100%)),
var(--color2, theme(colors.blue.700 / 40%)) 25%,
var(--color3, theme(colors.blue.900 / 15%)) 50%
);
.card:hover & {
scale: 1 1;
}
.group:hover & {
opacity: 100%;
}
}
}
.purpleGradient {
--color1: theme(colors.purple.400 / 90%);
--color2: theme(colors.indigo.800 / 90%);
--color3: theme(colors.indigo.800 / 20%);
}
</style>

212
src/routes/FeaturesSlice.svelte Executable file
View file

@ -0,0 +1,212 @@
<script context="module">
export const mouseContext = Symbol('mouseContext')
</script>
<script>
import FeatureCard from './FeatureCard.svelte'
import { setContext, onMount } from 'svelte'
import { writable } from 'svelte/store'
import PluginsIcon from '~icons/gg/arrange-back'
import ShortcutsIcon from '~icons/gg/push-chevron-right-o'
import TouchpadIcon from '~icons/gg/touchpad'
import Title from '$lib/components/Title.svelte'
import Hypractive from './Hypractive.svelte'
import { getIsMobile } from '$lib/Helper.mjs'
import configDefaultImage from '$lib/images/features/config_default.webp'
import configHoverImage from '$lib/images/features/config_hover.webp'
import smoothDefaultImage from '$lib/images/features/smooth_default.webp'
import smoothHoverImage from '$lib/images/features/smooth_hover.webp'
import tileDefaultImage from '$lib/images/features/tiling_default.webp'
import tileHoverImage from '$lib/images/features/tiling_hover.webp'
let isMobile = false
const context = setContext(mouseContext, {
x: writable(0),
y: writable(0),
isHoverCards: writable(false)
})
/** @type HTMLDivElement */
let featuresContainer
onMount(() => {
isMobile = getIsMobile()
})
function onMouseEnter() {
featuresContainer.addEventListener('mousemove', trackMouse)
context.isHoverCards.set(true)
}
function onMouseLeave() {
featuresContainer.removeEventListener('mousemove', trackMouse)
context.isHoverCards.set(false)
}
function trackMouse({ clientX, clientY }) {
context.x.set(clientX)
context.y.set(clientY)
}
</script>
<section class="relative flex flex-col items-center px-3 md:px-8">
<Title>
<span slot="pre">TLDR</span>
<span slot="title">Features</span>
</Title>
<div
class="group grid w-full flex-wrap gap-6 text-lg font-medium text-white/70 lg:grid-cols-2 lg:grid-rows-2"
role="contentinfo"
on:mouseenter={!isMobile && onMouseEnter}
on:mouseleave={!isMobile && onMouseLeave}
bind:this={featuresContainer}
>
<FeatureCard title="Smooth" class="row-span-2" color="purple">
<p class="max-w-[60ch]">
Smooth transitions. Great animations. High performance. Instant input.
</p>
<div class="_wrapper absolute inset-0 select-none" aria-hidden>
<div class="feature-image">
<img
src={smoothDefaultImage}
class="feature-image_inner"
alt=""
aria-hidden="true"
loading="lazy"
/>
<img
src={smoothHoverImage}
class="feature-image_inner-hover"
alt=""
aria-hidden="true"
loading="lazy"
/>
</div>
</div>
</FeatureCard>
<FeatureCard title="Easy to configure" color="purple">
<p class="max-w-[60ch]">
Live reloading config. Easy plain-text format. Sensible defaults. Great documentation.
</p>
<div class="_wrapper absolute inset-0 select-none" aria-hidden>
<div class="feature-image">
<img
src={configDefaultImage}
class="feature-image_inner"
alt=""
aria-hidden="true"
loading="lazy"
/>
<img
src={configHoverImage}
class="feature-image_inner-hover"
alt=""
aria-hidden="true"
loading="lazy"
/>
</div>
</div>
</FeatureCard>
<FeatureCard class="" title="Dynamic tiling" color="purple">
<p class="max-w-[60ch]">
Automatic tiling that just works. Supports multiple fine-tuneable layouts.
</p>
<div class="_wrapper absolute inset-0 select-none" aria-hidden>
<div class="feature-image">
<img
src={tileDefaultImage}
class="feature-image_inner"
alt=""
aria-hidden="true"
loading="lazy"
/>
<img
src={tileHoverImage}
class="feature-image_inner-hover"
alt=""
aria-hidden="true"
loading="lazy"
/>
</div>
</div>
</FeatureCard>
</div>
<div class="z-10 mt-14 flex flex-col flex-wrap justify-center gap-8 text-lg sm:flex-row">
<a
href="https://github.com/hyprland-community/awesome-hyprland#plugins"
target="_blank"
class="icon-feature hover:underline"
>
<PluginsIcon class="h-8 w-8" />
Plugin system
</a>
<Hypractive />
<a
href="https://wiki.hyprland.org/Configuring/Binds/#global-keybinds"
target="_blank"
class="icon-feature hover:underline"
>
<ShortcutsIcon class="h-8 w-8" />
Global shortcuts for apps
</a>
<a
href="https://wiki.hyprland.org/Configuring/Variables/#gestures"
class="icon-feature hover:underline"
target="_blank"
>
<TouchpadIcon class="h-8 w-8" />Touchpad gestures
</a>
</div>
</section>
<style lang="postcss">
.icon-feature {
@apply flex items-center justify-center gap-3 font-bold text-slate-400;
}
.feature-image {
position: absolute;
inset: 0 0 0 0;
opacity: 0.5;
z-index: -10;
display: flex;
align-items: center;
justify-content: center;
._wrapper:hover & {
opacity: 1;
}
& img {
position: absolute;
transition: opacity 1500ms ease-in-out;
pointer-events: none;
width: 400px;
aspect-ratio: 1;
right: -80px;
top: 50%;
translate: 0px -50%;
@media screen(md) {
width: 600px;
right: -80px;
}
}
}
.feature-image_inner-hover {
opacity: 0;
}
._wrapper:hover {
& .feature-image_inner {
opacity: 0;
}
& .feature-image_inner-hover {
opacity: 1 !important;
filter: saturate(1.3);
}
}
</style>

Some files were not shown because too many files have changed in this diff Show more