Redesign common styles and components in web

This commit is contained in:
Mathias Wagner
2026-01-18 02:03:06 +01:00
parent 474ed1d78f
commit b6b165a330
8 changed files with 408 additions and 252 deletions

View File

@@ -1,11 +1,15 @@
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import "./styles.sass";
export const Button = ({color, text, icon, onClick, disabled}) => {
export const Button = ({color = "primary", text, icon, onClick, disabled, size = "md", variant = "filled"}) => {
return (
<button className={`btn btn-color-${color}`} onClick={onClick} disabled={disabled}>
<FontAwesomeIcon icon={icon}/>
<h1>{text}</h1>
<button
className={`btn btn-${variant} btn-${color} btn-${size}`}
onClick={onClick}
disabled={disabled}
>
{icon && <FontAwesomeIcon icon={icon}/>}
{text && <span>{text}</span>}
</button>
);
}

View File

@@ -1,40 +1,116 @@
@use "@/common/styles/colors" as *
.btn
padding: 0.5rem 1rem
border-radius: 0.8rem
border: 1px solid transparent
display: flex
display: inline-flex
align-items: center
transition: all 0.2s ease-in-out
justify-content: center
gap: 0.5rem
font-family: inherit
font-weight: 600
border: none
border-radius: 8px
cursor: pointer
transition: all 0.2s ease
white-space: nowrap
.btn:hover
transform: translateY(-2px)
filter: brightness(1.2)
&:disabled
opacity: 0.5
cursor: not-allowed
.btn:active
transform: translateY(0) scale(0.98)
svg
font-size: 1em
.btn svg
margin-right: 10px
font-size: 18pt
.btn-sm
padding: 0.5rem 1rem
font-size: 0.875rem
.btn h1
margin: 0
font-weight: 900
.btn-md
padding: 0.75rem 1.5rem
font-size: 0.9375rem
.btn-color-red
background-color: rgba($red, 0.1)
color: $red
border: 1px solid $red
.btn-lg
padding: 1rem 2rem
font-size: 1rem
.btn-color-green
background-color: rgba($primary, 0.1)
color: $primary
.btn-filled
&.btn-primary
background: $primary
color: $background
&:hover:not(:disabled)
background: $primary-light
transform: translateY(-1px)
&:active:not(:disabled)
transform: translateY(0)
&.btn-blue
background: $blue
color: white
&:hover:not(:disabled)
background: $blue-light
transform: translateY(-1px)
&.btn-red
background: $red
color: white
&:hover:not(:disabled)
background: $red-light
transform: translateY(-1px)
&.btn-green
background: $primary
color: $background
&:hover:not(:disabled)
background: $primary-light
transform: translateY(-1px)
.btn-outline
background: transparent
&.btn-primary
border: 1px solid $primary
color: $primary
.btn-color-blue
background-color: rgba($blue, 0.1)
&:hover:not(:disabled)
background: rgba($primary, 0.1)
&.btn-blue
border: 1px solid $blue
color: $blue
border: 1px solid $blue
&:hover:not(:disabled)
background: rgba($blue, 0.1)
&.btn-red
border: 1px solid $red
color: $red
&:hover:not(:disabled)
background: rgba($red, 0.1)
&.btn-green
border: 1px solid $primary
color: $primary
&:hover:not(:disabled)
background: rgba($primary, 0.1)
.btn-ghost
background: transparent
border: none
&.btn-primary
color: $primary
&:hover:not(:disabled)
background: rgba($primary, 0.1)
&.btn-blue
color: $blue
&:hover:not(:disabled)
background: rgba($blue, 0.1)

View File

@@ -1,76 +1,84 @@
import "./styles.sass";
import Logo from "@/common/assets/logo192.png";
import Button from "@/common/components/Button";
import {faBars, faBarsStaggered, faHeart} from "@fortawesome/free-solid-svg-icons";
import {Link} from "react-router-dom";
import {faXmark, faBars, faArrowUpRightFromSquare} from "@fortawesome/free-solid-svg-icons";
import {faGithub} from "@fortawesome/free-brands-svg-icons";
import {Link, useLocation} from "react-router-dom";
import {FontAwesomeIcon} from "@fortawesome/react-fontawesome";
import {useEffect, useState} from "react";
import {useState, useEffect} from "react";
import {DOCUMENTATION_BASE} from "@/main.jsx";
export const DONATION_LINK = "https://www.ko-fi.com/gnmyt";
export const GITHUB_LINK = "https://github.com/gnmyt/myspeed";
export const Navigation = () => {
const [mobileOpen, setMobileOpen] = useState(true);
const [showScreen, setShowScreen] = useState(false);
const [mobileOpen, setMobileOpen] = useState(false);
const [scrolled, setScrolled] = useState(false);
const location = useLocation();
useEffect(() => {
const timeout = setTimeout(() => {
setMobileOpen(false);
}, 500);
return () => clearTimeout(timeout);
const handleScroll = () => {
setScrolled(window.scrollY > 20);
};
window.addEventListener("scroll", handleScroll);
return () => window.removeEventListener("scroll", handleScroll);
}, []);
const onAnimationEnd = (event) => {
if (event.animationName === "navClose") {
setShowScreen(false);
}
}
useEffect(() => {
setMobileOpen(false);
}, [location]);
const openMenu = () => {
setMobileOpen(true);
setShowScreen(true);
}
const isActive = (path) => location.pathname === path;
return (
<>
<nav>
<nav className={scrolled ? "nav-scrolled" : ""}>
<Link className="logo-area" to="/">
<img src={Logo} alt="Logo"/>
<h1>MySpeed</h1>
<img src={Logo} alt="MySpeed Logo"/>
<span>MySpeed</span>
</Link>
<div className="nav-area">
<ul>
<li><Link to="/install">Install</Link></li>
<li><Link to="/tutorials">Tutorials</Link></li>
<li><Link to={DOCUMENTATION_BASE}>Documentation</Link></li>
</ul>
<Button icon={faHeart} text="Donate" color="red"
onClick={() => window.open(DONATION_LINK, "_blank")}/>
<div className="nav-links">
<Link to="/" className={isActive("/") ? "active" : ""}>Home</Link>
<Link to="/install" className={isActive("/install") ? "active" : ""}>Install</Link>
<Link to="/tutorials" className={isActive("/tutorials") ? "active" : ""}>Tutorials</Link>
<a href={DOCUMENTATION_BASE} target="_blank" rel="noopener noreferrer" className="external-link">
Docs
<FontAwesomeIcon icon={faArrowUpRightFromSquare}/>
</a>
</div>
<div className="mobile-toggle" onClick={openMenu}>
<FontAwesomeIcon icon={mobileOpen ? faBarsStaggered : faBars}/>
<div className="nav-actions">
<a href={GITHUB_LINK} target="_blank" rel="noopener noreferrer" className="github-link">
<FontAwesomeIcon icon={faGithub}/>
<span>GitHub</span>
</a>
</div>
<button className="mobile-toggle" onClick={() => setMobileOpen(!mobileOpen)} aria-label="Toggle menu">
<FontAwesomeIcon icon={mobileOpen ? faXmark : faBars}/>
</button>
</nav>
<div className={`mobile-nav ${mobileOpen ? "" : "mobile-nav-closed"}`}
onClick={() => setMobileOpen(false)} onAnimationEnd={onAnimationEnd}>
<Link className={`logo${showScreen ? "" : " logo-pulse"}`} to="/">
<img src={Logo} alt="Logo"/>
<h1>MySpeed</h1>
</Link>
{showScreen && <>
<div className={`mobile-nav ${mobileOpen ? "mobile-open" : ""}`}>
<div className="mobile-content">
<Link to="/" className="mobile-logo">
<img src={Logo} alt="MySpeed Logo"/>
<span>MySpeed</span>
</Link>
<div className="mobile-links">
<Link to={"/install"}>Install</Link>
<Link to={"/tutorials"}>Tutorials</Link>
<Link to={DOCUMENTATION_BASE}>Documentation</Link>
<Link to="/" className={isActive("/") ? "active" : ""}>Home</Link>
<Link to="/install" className={isActive("/install") ? "active" : ""}>Install</Link>
<Link to="/tutorials" className={isActive("/tutorials") ? "active" : ""}>Tutorials</Link>
<a href={DOCUMENTATION_BASE} target="_blank" rel="noopener noreferrer">
Docs
<FontAwesomeIcon icon={faArrowUpRightFromSquare}/>
</a>
<a href={GITHUB_LINK} target="_blank" rel="noopener noreferrer">GitHub</a>
</div>
<Button icon={faHeart} text="Donate" color="red"
onClick={() => window.open(DONATION_LINK, "_blank")}/>
</>
}
</div>
</div>
{mobileOpen && <div className="mobile-overlay" onClick={() => setMobileOpen(false)}/>}
</>
);
}

View File

@@ -1,160 +1,182 @@
@use "@/common/styles/colors" as *
nav
display: flex
justify-content: space-between
padding: 0.5rem 2rem
background-color: rgba($background-lighter, 0.3)
backdrop-filter: blur(15px)
user-select: none
position: fixed
top: 0
left: 0
right: 0
height: 3rem
z-index: 10
z-index: 100
display: flex
align-items: center
justify-content: space-between
padding: 1rem 3rem
background: transparent
transition: all 0.3s ease
nav .logo-area
nav.nav-scrolled
background: rgba($background, 0.85)
backdrop-filter: blur(12px)
border-bottom: 1px solid $border-color
.logo-area
display: flex
align-items: center
gap: 0.75rem
text-decoration: none
transition: opacity 0.2s ease
&:hover
opacity: 0.7
img
width: 2rem
height: 2rem
span
font-size: 1.25rem
font-weight: 700
color: $text
.nav-links
display: flex
align-items: center
gap: 2.5rem
a
font-size: 0.9rem
font-weight: 500
color: $text-secondary
transition: color 0.2s ease
position: relative
&:hover, &.active
color: $text
&.active::after
content: ""
position: absolute
bottom: -4px
left: 0
right: 0
height: 2px
background: $primary
border-radius: 1px
.external-link
display: flex
align-items: center
gap: 0.375rem
svg
font-size: 0.625rem
opacity: 0.7
.nav-actions
display: flex
align-items: center
gap: 1rem
cursor: pointer
padding: 0.2rem 0.6rem
border: 2px solid transparent
border-radius: 1rem
transition: all 0.3s
text-decoration: none
.github-link
display: flex
align-items: center
gap: 0.5rem
padding: 0.5rem 1rem
background: $background-card
border: 1px solid $border-color
border-radius: 8px
color: $text-secondary
font-size: 0.875rem
font-weight: 500
transition: all 0.2s ease
&:hover
background: $background-lighter
border-color: $border-hover
color: $text
svg
font-size: 1rem
.mobile-toggle
display: none
background: none
border: none
color: $text
-webkit-user-drag: none
font-size: 1.25rem
cursor: pointer
padding: 0.5rem
.mobile-nav
position: fixed
top: 0
right: 0
width: 280px
height: 100vh
background: $background-lighter
z-index: 150
transform: translateX(100%)
transition: transform 0.3s ease
border-left: 1px solid $border-color
.mobile-nav.mobile-open
transform: translateX(0)
.mobile-content
padding: 2rem 1.5rem
display: flex
flex-direction: column
gap: 2rem
.mobile-logo
display: flex
align-items: center
gap: 0.75rem
text-decoration: none
padding-bottom: 1.5rem
border-bottom: 1px solid $border-color
img
width: 2.5rem
height: 2.5rem
h1
margin: 0
span
font-size: 1.5rem
font-weight: 700
color: $text
nav .logo-area:hover
border: 2px solid $text
nav .logo-area:active
transform: scale(0.95)
nav .nav-area
.mobile-links
display: flex
align-items: center
gap: 2rem
ul
display: flex
gap: 2rem
list-style: none
flex-direction: column
gap: 0.5rem
a
text-decoration: none
color: $text
font-size: 1.2rem
font-weight: 700
transition: color 0.3s
-webkit-user-drag: none
display: block
padding: 0.875rem 1rem
font-size: 1rem
font-weight: 500
color: $text-secondary
border-radius: 8px
transition: all 0.2s ease
a:hover
color: $primary
&:hover, &.active
background: rgba($primary, 0.1)
color: $primary
nav .mobile-toggle
display: none
align-items: center
justify-content: center
cursor: pointer
nav .mobile-toggle svg
font-size: 22pt
transition: all 0.3s
fill: $text
.mobile-nav
display: flex
.mobile-overlay
position: fixed
top: 0
left: 0
width: 100vw
height: 100%
-webkit-backdrop-filter: blur(1rem)
backdrop-filter: blur(1rem)
background: #00000080
z-index: 10
flex-direction: column
align-items: center
justify-content: center
gap: 2rem
animation: navOpen .3s forwards ease-in-out
inset: 0
background: rgba(0, 0, 0, 0.6)
backdrop-filter: blur(4px)
z-index: 140
.mobile-nav-closed
animation: navClose .3s forwards ease-in-out
@media (max-width: 768px)
nav
padding: 1rem 1.5rem
.mobile-nav .logo
display: flex
align-items: center
text-decoration: none
gap: 1rem
img
width: 3rem
height: 3rem
h1
margin: 0
font-size: 2rem
color: $text
.logo-pulse
animation: logoPulse 1s infinite
.mobile-nav .mobile-links
text-align: center
display: flex
flex-direction: column
list-style: none
a
text-decoration: none
color: $text
font-size: 1.7rem
font-weight: 700
transition: color 0.3s
-webkit-user-drag: none
a:hover
color: $primary
@keyframes navOpen
from
transform: translateY(100%)
visibility: hidden
to
transform: translateY(0)
visibility: visible
@keyframes navClose
from
transform: translateY(0)
visibility: visible
to
transform: translateY(-100%)
visibility: hidden
@keyframes logoPulse
0%
transform: scale(1)
50%
transform: scale(1.1)
100%
transform: scale(1)
@media (max-width: 814px)
nav .nav-area
.nav-links, .nav-actions
display: none
nav .mobile-toggle
display: flex
.mobile-toggle
display: flex
align-items: center
justify-content: center

View File

@@ -1,34 +1,14 @@
import {Outlet} from "react-router-dom";
import Navigation from "@/common/components/Navigation";
import "./styles.sass";
import {useEffect} from "react";
export const Root = () => {
useEffect(() => {
const handleKeydown = (event) => {
if (event.ctrlKey && (event.key === "+" || event.key === "-")) event.preventDefault();
}
const handleWheel = (event) => {
if (event.ctrlKey) event.preventDefault();
}
document.addEventListener("keydown", handleKeydown);
document.addEventListener("wheel", handleWheel, {passive: false});
return () => {
document.removeEventListener("keydown", handleKeydown);
document.removeEventListener("wheel", handleWheel);
}
}, []);
return (
<>
<div className="app-root">
<Navigation />
<main>
<main className="main-content">
<Outlet/>
</main>
</>
</div>
);
}

View File

@@ -1,7 +1,26 @@
main
padding: 1rem
height: calc(100vh - 7rem)
@use "@/common/styles/colors" as *
@media screen and (max-width: 1000px)
main
padding: 0.2rem
.app-root
min-height: 100vh
display: flex
flex-direction: column
position: relative
&::before
content: ""
position: fixed
top: -30%
left: 50%
transform: translateX(-50%)
width: 150%
height: 80%
background: radial-gradient(ellipse at center, rgba($primary, 0.07) 0%, transparent 60%)
pointer-events: none
z-index: 0
.main-content
flex: 1
display: flex
flex-direction: column
position: relative
z-index: 1

View File

@@ -1,6 +1,24 @@
$background: #131A20
$background-lighter: #1E222F
$text: #E5E5E5
$primary: #45C65A
$blue: #456AC6
$red: #EC5555
$background: #0a0e12
$background-lighter: #141a22
$background-card: #1a222d
$text: #f0f2f5
$text-secondary: #8b95a5
$text-muted: #5a6577
$primary: #22c55e
$primary-light: #4ade80
$primary-dark: #16a34a
$blue: #3b82f6
$blue-light: #60a5fa
$red: #ef4444
$red-light: #f87171
$orange: #f59e0b
$purple: #8b5cf6
$border-color: rgba(255, 255, 255, 0.08)
$border-hover: rgba(255, 255, 255, 0.15)
$shadow-sm: 0 1px 2px rgba(0, 0, 0, 0.3)
$shadow-md: 0 4px 12px rgba(0, 0, 0, 0.4)
$shadow-lg: 0 8px 32px rgba(0, 0, 0, 0.5)
$shadow-glow: 0 0 40px rgba($primary, 0.15)

View File

@@ -1,18 +1,47 @@
@use "colors" as *
body, html
*
box-sizing: border-box
margin: 0
padding: 0
body, html
background-color: $background
color: $text
font-family: "Inter", sans-serif
font-family: "Inter", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif
font-weight: 400
line-height: 1.6
-webkit-font-smoothing: antialiased
-moz-osx-font-smoothing: grayscale
scroll-behavior: smooth
overflow-x: hidden
h1, h2, h3, h4, h5, h6
font-weight: 700
line-height: 1.2
color: $text
p
color: $text-secondary
a
color: inherit
text-decoration: none
transition: color 0.2s ease
::selection
background: rgba($primary, 0.3)
color: $text
::-webkit-scrollbar
width: 13px
width: 8px
::-webkit-scrollbar-track
background: $background
::-webkit-scrollbar-thumb
filter: brightness(0.7)
border-radius: 10px
background: $background-card
border-radius: 4px
::-webkit-scrollbar-thumb:hover
filter: brightness(0.8)
background: $text-muted