Dev build

This commit is contained in:
EIiias 2025-05-01 23:07:22 +02:00
parent fa5fc6685c
commit 53fed5f4db
4 changed files with 207 additions and 9 deletions

BIN
public/discord-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 56 KiB

BIN
public/signal-logo.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 136 KiB

View File

@ -131,3 +131,121 @@ body {
color: rgba(255, 255, 255, 0.7);
font-size: 1rem;
}
.animated-paragraph {
font-size: 1.15rem;
color: #e0e0e0;
margin: 24px 0 32px 0;
text-align: center;
max-width: 600px;
margin-left: auto;
margin-right: auto;
letter-spacing: 0.01em;
line-height: 1.6;
}
.animated-paragraph .cursor {
display: inline-block;
width: 0.5em;
height: 1.2em;
background-color: #e0e0e0;
margin-left: 2px;
animation: blink 1s step-end infinite;
vertical-align: middle;
}
.modal-backdrop {
position: fixed;
top: 0; left: 0; right: 0; bottom: 0;
background: rgba(0,0,0,0.7);
z-index: 1000;
display: flex;
align-items: center;
justify-content: center;
}
.modal-content {
background: rgba(35, 39, 47, 0.85);
border-radius: 16px;
padding: 32px 24px 24px 24px;
box-shadow: 0 8px 32px rgba(0,0,0,0.45);
position: relative;
min-width: 340px;
max-width: 380px;
width: 100%;
max-height: 90vh;
display: flex;
flex-direction: column;
align-items: center;
border: 1.5px solid rgba(255,255,255,0.12);
backdrop-filter: blur(16px);
-webkit-backdrop-filter: blur(16px);
}
.modal-image {
max-width: 320px;
width: 100%;
height: auto;
border-radius: 12px;
margin-bottom: 16px;
background: #fff;
}
.modal-caption {
color: #e0e0e0;
font-size: 1.1rem;
margin-top: 8px;
text-align: center;
}
.modal-close {
position: absolute;
top: 12px;
right: 16px;
background: none;
border: none;
font-size: 2rem;
color: #aaa;
cursor: pointer;
transition: color 0.2s;
}
.modal-close:hover {
color: #EA4335;
}
.discord-tag-row {
display: flex;
align-items: center;
gap: 1rem;
justify-content: center;
margin-top: 8px;
}
.discord-tag {
font-size: 1.3rem;
font-family: 'Courier New', monospace;
color: #e0e0e0;
background: #23272f;
padding: 6px 16px;
border-radius: 8px;
border: 1px solid #5865F2;
letter-spacing: 0.03em;
}
.copy-btn {
background: #5865F2;
color: #fff;
border: none;
border-radius: 6px;
padding: 6px 16px;
font-size: 1rem;
cursor: pointer;
transition: background 0.2s, color 0.2s;
font-family: inherit;
}
.copy-btn:hover, .copy-btn:focus {
background: #4752c4;
color: #fff;
}

View File

@ -1,7 +1,7 @@
import './App.css'
import { useState, useEffect } from 'react'
function TypeWriter({ text }: { text: string }) {
function TypeWriter({ text, onComplete }: { text: string, onComplete: () => void }) {
const [displayText, setDisplayText] = useState('')
const [currentIndex, setCurrentIndex] = useState(0)
@ -10,10 +10,12 @@ function TypeWriter({ text }: { text: string }) {
const timeout = setTimeout(() => {
setDisplayText(prev => prev + text[currentIndex])
setCurrentIndex(prev => prev + 1)
}, 100)
}, 200)
return () => clearTimeout(timeout)
} else {
onComplete()
}
}, [currentIndex, text])
}, [currentIndex, text, onComplete])
return (
<div>
@ -23,14 +25,41 @@ function TypeWriter({ text }: { text: string }) {
)
}
function SocialCard({ title, icon, description, href }: {
function AnimatedParagraph({ text, startTyping }: { text: string, startTyping: boolean }) {
const [displayText, setDisplayText] = useState('')
const [currentIndex, setCurrentIndex] = useState(0)
const [isTyping, setIsTyping] = useState(false)
useEffect(() => {
if (startTyping && currentIndex < text.length) {
setIsTyping(true)
const timeout = setTimeout(() => {
setDisplayText(prev => prev + text[currentIndex])
setCurrentIndex(prev => prev + 1)
}, 50)
return () => clearTimeout(timeout)
} else if (currentIndex >= text.length) {
setIsTyping(false)
}
}, [currentIndex, text, startTyping])
return (
<p className="animated-paragraph">
{displayText}
{isTyping && <span className="cursor"></span>}
</p>
)
}
function SocialCard({ title, icon, description, href, onClick }: {
title: string,
icon: string,
description: string,
href: string
href: string,
onClick?: () => void
}) {
return (
<a href={href} className="social-card" target="_blank" rel="noopener noreferrer">
<a href={href} className="social-card" target="_blank" rel="noopener noreferrer" onClick={e => { if (onClick) { e.preventDefault(); onClick(); } }}>
<div className="social-card-content">
<img src={icon} alt={title} className="social-card-icon" />
<h3>{title}</h3>
@ -40,16 +69,64 @@ function SocialCard({ title, icon, description, href }: {
)
}
function SignalPopup({ show, onClose }: { show: boolean, onClose: () => void }) {
if (!show) return null
return (
<div className="modal-backdrop" onClick={onClose}>
<div className="modal-content" onClick={e => e.stopPropagation()}>
<button className="modal-close" onClick={onClose}>&times;</button>
<img src="/shikoro-signal.png" alt="Signal QR" className="modal-image" />
<div className="modal-caption">Scan to chat with me on Signal!</div>
</div>
</div>
)
}
function DiscordPopup({ show, onClose, tag }: { show: boolean, onClose: () => void, tag: string }) {
const [copied, setCopied] = useState(false)
if (!show) return null
const handleCopy = () => {
navigator.clipboard.writeText(tag)
setCopied(true)
setTimeout(() => setCopied(false), 1200)
}
return (
<div className="modal-backdrop" onClick={onClose}>
<div className="modal-content" onClick={e => e.stopPropagation()}>
<button className="modal-close" onClick={onClose}>&times;</button>
<img src="/discord-logo.png" alt="Discord Logo" className="modal-image" style={{background:'#5865F2',padding:'16px'}} />
<div className="modal-caption" style={{marginBottom:'12px'}}>Add me on Discord:</div>
<div className="discord-tag-row">
<span className="discord-tag">{tag}</span>
<button className="copy-btn" onClick={handleCopy}>{copied ? 'Copied!' : 'Copy'}</button>
</div>
</div>
</div>
)
}
function App() {
const [titleComplete, setTitleComplete] = useState(false)
const [showSignalPopup, setShowSignalPopup] = useState(false)
const [showDiscordPopup, setShowDiscordPopup] = useState(false)
return (
<div className="about-container">
<TypeWriter text="Shikoro.dev" />
<TypeWriter
text="Shikoro.dev"
onComplete={() => setTitleComplete(true)}
/>
<AnimatedParagraph
text="Hi, I like doing all sorts of IT projects. If you're interested in collaborating, feel free to contact me."
startTyping={titleComplete}
/>
<div className="social-cards">
<SocialCard
title="Discord"
icon="https://placehold.co/64x64/5865F2/FFFFFF/png?text=D"
icon="/discord-logo.png"
description="Add me on discord"
href="#"
onClick={() => setShowDiscordPopup(true)}
/>
<SocialCard
title="Email"
@ -59,11 +136,14 @@ function App() {
/>
<SocialCard
title="Signal"
icon="https://placehold.co/64x64/00AFF0/FFFFFF/png?text=S"
icon="/signal-logo.png"
description="Message me on signal"
onClick={() => setShowSignalPopup(true)}
href="#"
/>
</div>
<SignalPopup show={showSignalPopup} onClose={() => setShowSignalPopup(false)} />
<DiscordPopup show={showDiscordPopup} onClose={() => setShowDiscordPopup(false)} tag="shikoro.1" />
</div>
)
}