COMPLETE REDESIGN

This commit is contained in:
Parker M. 2024-01-12 23:21:25 -06:00
parent 46b32793f2
commit dbfb7fd415
No known key found for this signature in database
GPG Key ID: 95CD2E0C7E329F2A
15 changed files with 675 additions and 927 deletions

View File

@ -1,3 +1,7 @@
# [PKRM.DEV](https://pkrm.dev) # [PKRM.DEV](https://pkrm.dev)
This is the repo for my personal website ([pkrm.dev](https://pkrm.dev)). Feel free to fork this project or download the code and use it for your needs. Happy coding! This is the repo for my personal website ([pkrm.dev](https://pkrm.dev)).
Heavily inspired by the "Hello Friend NG" HUGO Theme, found [here](https://github.com/rhazdon/hugo-theme-hello-friend-ng).
Feel free to fork this project or download the code and use it for your needs. Happy coding!

Binary file not shown.

Before

Width:  |  Height:  |  Size: 186 KiB

BIN
app/static/github.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

BIN
app/static/gnugpg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

BIN
app/static/gnupg.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.8 KiB

View File

@ -1,451 +0,0 @@
* {
font-family: 'Kanit', sans-serif;
margin: 0;
scroll-behavior: smooth;
}
body{
overflow-x: hidden;
background-image: url(/static/bg.png);
background-size: cover;
color: #fff;
will-change: transform;
}
.fa-angle-down {
color: #fff;
font-size: 1.823vw;
display: flex;
justify-content: center;
align-items: center;
position: absolute;
bottom: 5.208vw;
}
.fa-angle-down:hover {
opacity: .6;
cursor: pointer;
}
/* Fixed footer */
footer {
position: sticky;
bottom: 0;
background-color: #0d0d0d;
font-size: 1.042vw;
display: flex;
justify-content: center;
align-items: center;
z-index: 10;
padding: 1.042vw;
}
footer a {
text-decoration: underline;
color: white
}
footer a:hover {
opacity: .6;
cursor: pointer;
}
/* Media query for footer on tablets */
@media screen and (max-width: 1280px) {
footer {
font-size: 2.5vw;
}
}
/* Media query for footer on mobile */
@media screen and (max-width: 600px) {
footer {
font-size: 5vw;
}
}
/* Navbar mobile icons */
#hamburger i {
font-size: 2.083vw;
position: absolute;
top: 40px;
right: 40px;
display: none;
}
#x i {
font-size: 2.083vw;
position: absolute;
top: 40px;
right: 40px;
display: none;
}
/* Main page (Hi I'm Parker) */
#main {
height: 100vh;
width: 100vw;
position: relative;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
nav {
display: flex;
justify-content: center;
align-items: center;
}
nav a {
text-decoration: none;
color: #fff;
font-size: 1.823vw;
margin: 0 25px;
}
nav a:hover {
opacity: .6;
cursor: pointer;
}
.icons {
display: flex;
justify-content: center;
align-items: center;
}
.icons i {
font-size: 3.125vw;
margin: 1.042vw 50px;
}
.icons i:hover {
opacity: .6;
cursor: pointer;
}
header {
text-align: center;
position: relative;
display: flex;
justify-content: center;
align-items: center;
}
header h1 {
font-size: 8.333vw;
}
header h2 {
font-size: 3.125vw;
/* animation: bringIn 2s; */
padding-right: 50px;
}
/* Main section media query for tablets */
@media screen and (max-width: 1280px) {
nav a {
font-size: 4vw;
margin-bottom: 3.646vw;
}
header h1 {
font-size: 12vw;
}
header h2 {
font-size: 7vw;
padding-right: 25px;
}
.icons {
margin-top: 3vw;
}
.icons i {
font-size: 7vw;
margin: 0 25px;
}
.fa-angle-down {
font-size: 5vw;
bottom: 15vw;
}
}
/* Main section media query for mobile */
@media screen and (max-width: 600px) {
#hamburger i,
#x i {
font-size: 8vw;
}
nav {
flex-direction: column;
position: absolute;
}
nav a {
font-size: 7vw;
margin-bottom: 15vw;
}
header {
flex-direction: column;
bottom: 100px;
}
header h1 {
font-size: 17vw;
}
header h2 {
font-size: 12vw;
padding-right: 0;
}
.icons {
flex-direction: row;
position: absolute;
bottom: 60vw;
}
.icons i {
font-size: 12vw;
margin: 0 5px;
}
.fa-angle-down {
font-size: 8vw;
bottom: 20vw;
}
}
/* Create the keyframes fade in */
@keyframes fadeIn {
0% { opacity: 0; }
100% { opacity: 1; }
}
@-webkit-keyframes fadeIn {
0% { opacity: 0; }
100% { opacity: 1; }
}
/* About me section */
#about {
height: 100vh;
width: 100vw;
position: relative;
display: flex;
justify-content: center;
align-items: center;
flex-direction: column;
}
#about h1 {
font-size: 4.167vw;
margin-bottom: 1.563vw;
}
#about p {
font-size: 1.563vw;
width: 50%;
text-align: center;
}
/* About section media query for tablets */
@media screen and (max-width: 1280px) {
#about h1 {
font-size: 10vw;
}
#about p {
font-size: 4vw;
width: 80%;
}
}
/* About section media query for mobile */
@media screen and (max-width: 600px) {
#about h1 {
font-size: 12vw;
}
#about p {
font-size: 5vw;
width: 80%;
}
}
/* Slider for Projects - Credit to this codepen - https://codepen.io/maheshambure21/pen/qZZrxy */
*, *:before, *:after {
box-sizing: border-box;
}
.slider {
height: 100vh;
position: relative;
overflow: hidden;
display: flex;
flex-flow: row nowrap;
align-items: flex-end;
justify-content: center;
}
.slider__nav {
width: 1.042vw;
height: 1.042vw;
margin: 1.042vw 0.521vw;
border-radius: 50%;
z-index: 10;
outline: 14px solid white;
outline-offset: -14px;
cursor: pointer;
appearance: none;
}
.slider__nav:checked {
-webkit-animation: check 0.4s linear forwards;
animation: check 0.4s linear forwards;
}
.slider__nav:checked:nth-of-type(1) ~ .slider__inner {
left: 0%;
}
.slider__nav:checked:nth-of-type(2) ~ .slider__inner {
left: -100%;
}
.slider__nav:checked:nth-of-type(3) ~ .slider__inner {
left: -200%;
}
.slider__nav:checked:nth-of-type(4) ~ .slider__inner {
left: -300%;
}
.slider__inner {
position: absolute;
top: 0;
left: 0;
width: 400%;
height: 100%;
-webkit-transition: left 0.7s;
transition: left 0.7s;
display: flex;
flex-flow: row nowrap;
}
.slider__contents {
height: 100%;
text-align: center;
display: flex;
flex: 1;
flex-flow: column nowrap;
align-items: center;
justify-content: center;
}
.slider__caption {
font-size: 4.167vw;
margin: 1.563vw;
max-width: 75%;
}
.slider__txt {
color: white;
max-width: 30%;
font-size: 1.563vw;
}
.slider__txt .inner-reference {
color: white;
text-decoration: underline;
}
.slider__button {
font-size: 1.563vw;
padding: 0.391vw 1.042vw;
border-radius: 5px;
background: #333;
color: white;
text-decoration: none;
cursor: pointer;
margin-right: 1.042vw;
}
.slider__button:hover {
opacity: .6;
transition: all .2s ease-in-out;
-webkit-transition: all .2s ease-in-out;
}
/* Projects section media query for tablets */
@media screen and (max-width: 1280px) {
.slider__caption {
max-width: 95%;
font-size: 10vw;
}
.slider__txt {
font-size: 4vw;
max-width: 80%;
}
.slider__button {
font-size: 4vw;
}
.slider__nav {
margin-bottom: 2.64vw;
width: 3vw;
height: 3vw;
margin: 0 2vw 10vw 2vw;
}
}
/* Projects section media query for mobile */
@media screen and (max-width: 600px) {
.slider__caption {
font-size: 12vw;
max-width: 95%;
}
.slider__txt {
font-size: 5vw;
max-width: 80%;
}
.slider__button {
font-size: 5vw;
display: flex;
justify-content: center;
margin: 0;
margin-bottom: 6vw;
}
.slider__nav {
margin-bottom: 2.64vw;
width: 5vw;
height: 5vw;
margin: 0 2vw 10vw 2vw;
}
}
@-webkit-keyframes check {
50% {
outline-color: #333;
}
100% {
outline-color: #333;
}
}
@keyframes check {
50% {
outline-color: #333;
}
100% {
outline-color: #333;
}
}

View File

@ -1,185 +0,0 @@
// Define the variables
const hamburger = document.querySelector('.fa-bars');
const x = document.querySelector('.fa-times');
const nav = document.querySelectorAll('nav')[0];
const navOptions = nav.querySelectorAll('a');
const main = document.getElementById('main');
const mainh1 = main.getElementsByTagName('h1')[0];
const mainh2 = main.getElementsByTagName('h2')[0];
const iconNav = document.querySelectorAll('nav')[1];
const icons = document.querySelectorAll('nav')[1].getElementsByTagName('i');
const arrow = document.getElementById('about-text')
const projects = document.getElementById('projects');
const footer = document.querySelector('footer');
// If the user is on mobile, remove the fingerprint from the footer, also remove
// the down arrow buttons
if (window.innerWidth <= 768) {
footer.removeChild(footer.lastChild);
arrow.style.display = 'none';
}
// If the user is on mobile
if (window.innerWidth < 768) {
x.style.display = 'none';
hamburger.style.display = 'block';
nav.style.display = 'none';
mobileAnimations();
} else {
x.style.display = 'none';
hamburger.style.display = 'none';
nav.style.display = 'flex';
desktopAnimations();
}
// Only create listener if the user is on mobile
if (window.innerWidth < 768) {
// When the hamburger is clicked, show the X and open the menu
hamburger.addEventListener('click', function () {
hamburger.style.display = 'none';
x.style.display = 'block';
mainh1.style.display = 'none';
mainh2.style.display = 'none';
iconNav.style.display = 'none';
arrow.style.display = 'none';
// Slowly fade in the nav with keyframes
nav.style.display = 'flex';
nav.style.animation = 'fadeIn 0.75s ease-in-out';
// Lock the scroll
document.body.style.overflow = 'hidden';
});
}
// Collapse the mobile nav menu
function collapseMenu() {
hamburger.style.display = 'block';
x.style.display = 'none';
nav.style.display = 'none';
// Slowly fade in the h1, h2, icons, and arrow with keyframes
mainh1.style.display = 'block';
mainh1.style.animation = 'fadeIn 0.75s ease-in-out';
mainh2.style.display = 'block';
mainh2.style.animation = 'fadeIn 0.75s ease-in-out';
iconNav.style.display = 'flex';
iconNav.style.animation = 'fadeIn 0.75s ease-in-out';
arrow.style.display = 'block';
arrow.style.animation = 'fadeIn 0.75s ease-in-out';
// Unlock the scroll
document.body.style.overflow = 'auto';
}
// Only create listener if the user is on mobile
if (window.innerWidth < 768) {
// When the X is clicked, show the hamburger and close the menu
x.addEventListener('click', function () {
collapseMenu();
});
}
// When a nav link is clicked, close the menu
const navLinks = document.querySelectorAll('nav a');
navLinks.forEach(function (link) {
link.addEventListener('click', function () {
// Only collapse the menu if the user is on mobile
if (window.innerWidth < 768) {
collapseMenu();
}
});
});
// Animation for the desktop version
function desktopAnimations() {
let everythingSlideUp = new mojs.Html({
el: main,
y: {150: 0},
duration: 1000,
delay: 250,
easing: 'sin.out'
}).play();
let everythingOpacity = new mojs.Html({
el: main,
opacity: {0: 1},
duration: 2000,
delay: 250,
easing: 'sin.out'
}).play();
}
// Animation for the mobile version
function mobileAnimations() {
let h2ScaleUp = new mojs.Html({
el: mainh2,
y: {75: 75},
opacity: {0: 1},
scale: {0.5: 1},
duration: 1500,
easing: mojs.easing.path('M0,100 C50,100 50,67.578125 50,50 C50,32.421875 50,0 100,0')
}).play();
let h2SlideUp = new mojs.Html({
el: mainh2,
y: {75: 0},
duration: 1500,
delay: 1000,
easing: mojs.easing.path('M0,100 C50,100 50,67.578125 50,50 C50,32.421875 50,0 100,0')
}).play();
let h1Opacity = new mojs.Html({
el: mainh1,
opacity: {0: 1},
duration: 2000,
delay: 1750,
easing: 'sin.out'
}).play();
let iconBringIn1 = new mojs.Html({
el: icons[0],
y: {25: 0},
opacity: {0: 1},
duration: 1000,
delay: 2000,
easing: 'sin.out'
}).play();
let iconBringIn2 = new mojs.Html({
el: icons[1],
y: {25: 0},
opacity: {0: 1},
duration: 1000,
delay: 2250,
easing: 'sin.out'
}).play();
}
// Rotate the projects every 4 seconds, only for desktop
window.onload = slideProjects();
function slideProjects() {
const inputs = document.querySelectorAll('input');
if (window.innerWidth >= 768) {
let i = 0;
setInterval(() => {
if (i == -1) {
return;
}
inputs[i].checked = true;
i++;
if (i >= inputs.length) {
i = 0;
}
}, 4000);
// If one of the inputs is clicked, stop the rotation of the projects
inputs.forEach(input => {
input.addEventListener('click', () => {
i = -1;
});
});
}
}

BIN
app/static/linkedin.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.0 KiB

BIN
app/static/mail.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.7 KiB

132
app/templates/about.html Normal file
View File

@ -0,0 +1,132 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home</title>
</head>
<body>
<nav>
<div class="navbar-left">
<a href="/">~$&nbsp;&nbsp;&nbsp;cd /home</a>
</div>
<div class="navbar-right">
<a href="/about">About</a>
<a href="/contact">Contact</a>
</div>
</nav>
<div class="container">
<h1>About Me</h1>
<p>I am a senior in high school who enjoys programming and learning more about different technologies. I manage a Debian home server where I self-host tools like: <a href="https://dnscrypt.org" target="_blank" style="text-decoration: underline; color: #9F9FAA">DNSCrypt</a>, <a href="https://github.com/AdguardTeam/AdguardHome" target="_blank" style="text-decoration: underline; color: #9F9FAA">AdGuard</a>, <a href="https://wireguard.org" target="_blank" style="text-decoration: underline; color: #9F9FAA">WireGuard</a>, <a href="https://syncthing.net" target="_blank" style="text-decoration: underline; color: #9F9FAA">Syncthing</a>, and many more media/system management utilities.<br><br>Find my work by viewing my open-sourced repositories on <a href="https://github.com/packetparker" target="_blank" style="text-decoration: underline; color: #9F9FAA">GitHub</a>.</p>
</div>
<footer>PGP Fingerprint: 58B7 6B8B BAB8 794D 21E2 579C 95CD 2E0C 7E32 9F2A</footer>
</body>
</html>
<style>
/* Import Montserrat font */
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@200;300;400;500;600;700&display=swap');
body {
margin: 0;
padding: 0;
font-family: 'Montserrat', sans-serif;
background-color: #242528;
color: #9F9FAA;
}
/* Navbar styles */
nav {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
background-color: #212223;
color: #9F9FAA;
}
nav a {
text-decoration: none;
color: #9F9FAA;
font-size: 1.5rem;
margin: 0 1rem;
}
nav a:hover {
color: #fff;
}
.navbar-left {
display: flex;
justify-content: flex-start;
align-items: center;
margin-left: 15%;
}
.navbar-right {
display: flex;
justify-content: flex-end;
align-items: center;
margin-right: 15%;
}
/* Navbar media query for mobile */
@media only screen and (max-width: 600px) {
nav a {
margin: 0;
font-size: 1rem;
}
.navbar-left {
margin: 0 1rem;
}
.navbar-right {
margin: 0 1rem;
}
.navbar-right a {
margin-left: 1rem;
}
}
/* Container styles */
.container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 1.75rem;
text-align: center;
width: 600px;
}
/* Continer media query for mobile */
@media only screen and (max-width: 600px) {
.container {
font-size: 1.25rem;
width: 300px;
}
.container p {
margin: 1rem 0 2rem 0;
}
}
/* Footer styles */
footer {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
font-size: 1rem;
text-align: center;
margin-bottom: 2rem;
width: 90vw;
}
</style>

View File

@ -2,245 +2,269 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Add .onion link --> <title>Home</title>
<meta http-equiv="onion-location" content="http://w6rshvdkwg3m757xjsk6avsbwphi3znwlvqpnbcchol6h7wxfzyabayd.onion/contact">
<link href="https://fonts.googleapis.com/css2?family=Kanit:wght@200;300;400;500;600;700&display=swap" rel="stylesheet">
<title>Contact | pkrm.dev</title>
<meta name="robots" content="index, follow">
<meta name="theme-color" content="#000" />
<meta name="apple-mobile-web-app-title" content="Parker M. | Contact" />
<meta property="og:title" content="Parker M. | Contact" />
<meta property="og:url" content="https://pkrm.dev" />
<meta property="og:type" content="website" />
<meta property="og:description" content="Fill out this form in order to contact me." />
<meta name="description" content="Fill out this form in order to contact me." />
<meta name="robots" content="index, follow" />
<meta name="http-equiv" content="X-Robots-Tag : index, follow" />
<meta name="googlebot" content="index, follow" />
<meta http-equiv="Onion-Location" content="http://dazz5im5t55mdzyfgegonv4d5nug2bt5nzck4m7zhqwynxo3xcvc77ad.onion/contact">
<meta name="twitter:card" content="summary" />
<script src="/static/contact.js" defer></script>
</head> </head>
<body> <body>
<a href="/" id="back-home">Back Home</a> <nav>
<div class="navbar-left">
<a href="/">~$&nbsp;&nbsp;&nbsp;cd /home</a>
</div>
<div class="navbar-right">
<a href="/about">About</a>
<a href="/contact">Contact</a>
</div>
</nav>
<div class="container"> <div class="container">
<p id="attention">ATTENTION: To send any sensitive information, please email me at <a href="mailto:contact@pkrm.dev" id="contact-email">contact@pkrm.dev</a> and encrypt the message with my <a href="/static/contact.asc" id="pgp-key">PGP public key</a>. This form should not be thought of as a secure way of communication.</p> <h1>Contact Me</h1>
<form actions="/contact" method="POST" name="contact" onsubmit="event.preventDefault(); checkForm();">
<input name="name" placeholder="Name"> <form action="/contact" method="POST">
<input name="email" placeholder="Email"> <input type="text" name="name" placeholder="Name">
<input type="text" name="email" placeholder="Email">
<textarea name="message" placeholder="Message"></textarea> <textarea name="message" placeholder="Message"></textarea>
<button>Send</button> <input type="submit" value="Submit">
</form> </form>
{% if success %} {% if success %}
<p class="success">Thank you for contacting me! I will get back to you soon.</p> <p class="success-message">Thank you for contacting me. I'll get back soon.</p>
{% endif %} {% endif %}
{% if error %} {% if error %}
<p class="error">There was an error sending your message. Please try again later.</p> <p class="error-message">An error occured, please try again later.</p>
{% endif %} {% endif %}
</div> </div>
<footer>PGP Fingerprint: 58B7 6B8B BAB8 794D 21E2 579C 95CD 2E0C 7E32 9F2A</footer>
</body> </body>
</html> </html>
<script>
const nameInput = document.querySelector('input[name="name"]');
const email = document.querySelector('input[name="email"]');
const message = document.querySelector('textarea[name="message"]');
window.onload = validateForm();
function validateForm() {
nameInput.addEventListener('input', () => {
if (nameInput.value.length > 0) {
nameInput.style.border = '2px solid #242424';
} else {
nameInput.style.border = '2px solid #FA5B5B';
}
});
email.addEventListener('input', () => {
if (email.value.length > 0) {
email.style.border = '2px solid #242424';
} else {
email.style.border = '2px solid #FA5B5B';
}
});
message.addEventListener('input', () => {
if (message.value.length > 0) {
message.style.border = '2px solid #242424';
} else {
message.style.border = '2px solid #FA5B5B';
}
});
}
function checkForm() {
// If nothing is empty, submit
if (nameInput.value.length > 0 && email.value.length > 0 && message.value.length > 0) {
document.forms['contact'].submit();
// Otherwise, outline the empty fields in red
} else {
if (nameInput.value.length == 0) {
nameInput.style.border = '2px solid #FA5B5B';
}
if (email.value.length == 0) {
email.style.border = '2px solid #FA5B5B';
}
if (message.value.length == 0) {
message.style.border = '2px solid #FA5B5B';
}
}
}
</script>
<style> <style>
/* Import Montserrat font */
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@200;300;400;500;600;700&display=swap');
* { * {
font-family: 'Kanit', sans-serif; font-family: 'Montserrat', sans-serif;
}
body {
margin: 0; margin: 0;
scroll-behavior: smooth; padding: 0;
background-color: #242528;
color: #9F9FAA;
} }
body{
overflow-x: hidden; /* Navbar styles */
background-image: url(/static/bg.png); nav {
background-size: cover; display: flex;
color: #fff; justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
background-color: #212223;
color: #9F9FAA;
} }
#back-home { nav a {
font-weight: bold;
position: absolute;
top: 0;
left: 50%;
transform: translateX(-50%);
padding-top: 2.604vw;
font-size: 2.083vw;
color: #fff;
text-decoration: none; text-decoration: none;
color: #9F9FAA;
font-size: 1.5rem;
margin: 0 1rem;
} }
a:hover { nav a:hover {
cursor: pointer; color: #fff;
opacity: 0.6;
} }
/* "Back Home" media query for mobile */ .navbar-left {
display: flex;
justify-content: flex-start;
align-items: center;
margin-left: 15%;
}
.navbar-right {
display: flex;
justify-content: flex-end;
align-items: center;
margin-right: 15%;
}
/* Navbar media query for mobile */
@media only screen and (max-width: 600px) { @media only screen and (max-width: 600px) {
#back-home { nav a {
font-size: 7vw; margin: 0;
padding-top: 4.167vw; font-size: 1rem;
width: 90%; }
display: flex;
justify-content: center; .navbar-left {
align-items: center; margin: 0 1rem;
}
.navbar-right {
margin: 0 1rem;
}
.navbar-right a {
margin-left: 1rem;
} }
} }
/* Container styles */
.container { .container {
position: absolute; position: absolute;
top: 50%; top: 50%;
left: 50%; left: 50%;
transform: translate(-50%, -50%); transform: translate(-50%, -50%);
font-size: 1.75rem;
text-align: center;
border: 1px solid #9F9FAA;
border-radius: 5px;
padding: 2rem;
height: 450px;
width: 550px;
} }
/* Container media query for mobile */ .container h1 {
margin: 0 0 1rem 0;
}
/* Continer media query for mobile */
@media only screen and (max-width: 600px) { @media only screen and (max-width: 600px) {
.container { .container {
position: absolute; font-size: 1.25rem;
top: 60%; width: 250px;
left: 50%; }
transform: translate(-50%, -50%);
.container h1 {
margin: 0 0 2rem 0;
} }
} }
#attention { /* Form styles */
font-size: 1.302vw;
text-align: center;
margin: 0.521vw;
}
/* Attention media query for mobile */
@media only screen and (max-width: 600px) {
#attention, #attention a {
font-size: 5vw;
}
}
#pgp-key, #contact-email {
color: #fff;
text-decoration: underline;
font-size: 1.302vw;
}
form { form {
display: flex; display: flex;
flex-direction: column; flex-direction: column;
border-radius: 10px; justify-content: center;
align-items: center;
} }
form input, form textarea, form button { input {
color: white; margin: 0.75rem 0;
background-color: #242424; padding: 0.5rem 1rem;
border: 2px solid #242424; border: 1px solid #9F9FAA;
border-radius: 10px; border-radius: 5px;
padding: 0.521vw; background-color: #242528;
margin: 0.521vw; color: #9F9FAA;
width: 50vw; font-size: 1.25rem;
font-size: 1.563vw; width: 500px;
resize: none;
} }
form button { input[type="submit"] {
width: 20vw; width: calc(500px + 2rem);
}
form button:hover {
cursor: pointer; cursor: pointer;
background-color: #333; }
textarea {
margin: 0.75rem 0;
padding: 0.5rem 1rem;
border: 1px solid #9F9FAA;
border-radius: 5px;
background-color: #242528;
color: #9F9FAA;
font-size: 1.25rem;
height: 100px;
width: 500px;
resize: none;
} }
/* Form media query for mobile */ /* Form media query for mobile */
@media only screen and (max-width: 600px) { @media only screen and (max-width: 600px) {
form input, form textarea, form button { input {
width: 70vw; width: 250px;
font-size: 7vw;
margin: 5px;
} }
form button { input[type="submit"] {
width: 50vw; width: calc(250px + 2rem);
margin-left: 50%; }
transform: translateX(-50%);
textarea {
width: 250px;
} }
} }
.success { /* Success/Error messages */
font-size: 1.302vw; .success-message {
color: #71A172;
font-size: 1.25rem;
margin-top: 0.5rem;
}
.error-message {
color: #B16161;
font-size: 1.25rem;
margin-top: 0.5rem;
}
/* Footer styles */
footer {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
font-size: 1rem;
text-align: center; text-align: center;
color: #73C276; margin-bottom: 2rem;
width: 90vw;
} }
</style>
<script>
// Get the form
const form = document.querySelector('form');
.error { // Get the inputs
font-size: 1.302vw; const nameInput = document.querySelector('input[name="name"]');
text-align: center; const email = document.querySelector('input[name="email"]');
color: #FA5B5B const message = document.querySelector('textarea[name="message"]');
}
/* Response media query for mobile */ // For each input, if it is empty, make the border red
@media only screen and (max-width: 600px) { form.addEventListener('submit', (e) => {
.error, if (nameInput.value === '') {
.success { e.preventDefault();
font-size: 3.125vw; nameInput.style.borderColor = '#B16161';
} }
}
</style> if (email.value === '') {
e.preventDefault();
email.style.borderColor = '#B16161';
}
if (message.value === '') {
e.preventDefault();
message.style.borderColor = '#B16161';
}
});
// If the user starts typing in an input, make the border grey
nameInput.addEventListener('input', () => {
nameInput.style.borderColor = '#9F9FAA';
});
email.addEventListener('input', () => {
email.style.borderColor = '#9F9FAA';
});
message.addEventListener('input', () => {
message.style.borderColor = '#9F9FAA';
});
// On form submit, make sure all inputs are filled out
form.addEventListener('submit', (e) => {
if (nameInput.value === '' || email.value === '' || message.value === '') {
e.preventDefault();
}
});
</script>

View File

@ -2,121 +2,164 @@
<html lang="en"> <html lang="en">
<head> <head>
<meta charset="UTF-8"> <meta charset="UTF-8">
<meta http-equiv="X-UA-Compatible" content="IE=edge">
<meta name="viewport" content="width=device-width, initial-scale=1.0"> <meta name="viewport" content="width=device-width, initial-scale=1.0">
<!-- Add .onion link --> <title>Home</title>
<meta http-equiv="onion-location" content="http://w6rshvdkwg3m757xjsk6avsbwphi3znwlvqpnbcchol6h7wxfzyabayd.onion/">
<!-- import font awesome through CDN link -->
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/6.3.0/css/all.min.css">
<link href="https://fonts.googleapis.com/css2?family=Kanit:wght@200;300;400;500;600;700&display=swap" rel="stylesheet">
<title>Parker | pkrm.dev</title>
<meta name="robots" content="index, follow">
<meta name="theme-color" content="#000" />
<meta name="apple-mobile-web-app-title" content="Parker M." />
<meta property="og:title" content="Parker M." />
<meta property="og:url" content="https://pkrm.dev" />
<meta property="og:type" content="website" />
<meta property="og:description" content="This is my website. Take a look around." />
<meta name="description" content="This is my website. Take a look around." />
<meta name="robots" content="index, follow" />
<meta name="http-equiv" content="X-Robots-Tag : index, follow" />
<meta name="googlebot" content="index, follow" />
<meta name="twitter:card" content="summary" />
<link rel="stylesheet" href="static/index.css">
<script src="/static/index.js" defer></script>
</head> </head>
<body> <body>
<section id="main"> <nav>
<a id="hamburger"><i class="fa-solid fa-bars"></i></a> <div class="navbar-left">
<a id="x"><i class="fa-solid fa-times"></i></a> <a href="/">~$&nbsp;&nbsp;&nbsp;cd /home</a>
<nav>
<a href="#home">Home</a>
<a href="#about">About</a>
<!-- Send to 'end' otherwise the carousel points at the bottom won't appear because of
the footer covering them -->
<a href="#end">Projects</a>
<a href="/contact">Contact</a>
</nav>
<header>
<h2>Hi, I'm</h2>
<h1>PARKER</h1>
</header>
<nav class="icons">
<a href="https://github.com/PacketParker" target="_blank"><i class="fa-brands fa-github"></i></a>
<a href="mailto:contact@pkrm.dev" target="_blank"><i class="fa-solid fa-envelope"></i></a>
</nav>
<a href="#about" id="about-text"><i class="fa fa-angle-down"></i></a>
</section>
<section id="about">
<h1>About Me</h1>
<p>I am a Senior in high school and enjoy learning about new technologies. I currently use HTML/CSS, Python, Javascript, and SQL. I have competed at the district and state levels in SkillsUSA for Cybersecurity and Programming, and have placed 2nd for the Congressional App Challenge. This year I plan on learning more about web3 and artificial intelligence.</p>
<!-- Link to 'end' rather than 'projects' otherwise it wouldn't go to the bottom completely -->
<a href="#end" id="projects-text"><i class="fa fa-angle-down"></i></a>
</section>
<section id="projects">
<div class="slider">
<input type="radio" name="slider" title="slide1" checked="checked" class="slider__nav"/>
<input type="radio" name="slider" title="slide2" class="slider__nav"/>
<input type="radio" name="slider" title="slide3" class="slider__nav"/>
<input type="radio" name="slider" title="slide4" class="slider__nav"/>
<div class="slider__inner">
<div class="slider__contents">
<h2 class="slider__caption">PeakPass</h2>
<p class="slider__txt">
PeakPass is a password manager that also doubles as a tool to check your passwords against those that have been affected in previous data breaches. Along with this we offer a blog to teach users about standards that keep them safe online.
<br>
<br>
<a href="https://github.com/peakpass/peakpass" target="_blank" referrerpolicy="no-referrer" class="slider__button">Source Code</a>
<a href="https://peakpass.pkrm.dev" target="_blank" referrerpolicy="no-referrer" class="slider__button">View Site</a>
</p>
</div>
<div class="slider__contents">
<h2 class="slider__caption">Aqua Bot</h2>
<p class="slider__txt">
A multipurpose Discord bot made with the discord.py 2.0 library. Aqua Bot has features like moderation, an economy, gambling. In addition to this, users can play music from YouTube, Spotify, and SoundCloud thanks to lavalink.py
<br>
<br>
<a href="https://github.com/PacketParker/aquabot" target="_blank" referrerpolicy="no-referrer" class="slider__button">Source Code</a>
</p>
</div>
<div class="slider__contents">
<h2 class="slider__caption">VaultCheck</h2>
<p class="slider__txt">
Uses the bitwarden-cli tool, the Twilio API, and the HaveIBeenPwned API in order to check your vault for leaked passwords. If a compromised password is found, it alerts you with a text message.
<br>
<br>
<a href="https://github.com/PacketParker/bitwarden-password-checker" target="_blank" referrerpolicy="no-referrer" class="slider__button">Source Code</a>
</p>
</div>
<div class="slider__contents">
<h2 class="slider__caption">Messagearr</h2>
<p class="slider__txt">
Docker container that has integration with <a href="https://radarr.video/" class="inner-reference">Radarr</a> in order to allow users to request movies to be downloaded through text messaging. Currently supports the Twilio and Telnyx messaging APIs.
<br>
<br>
<a href="https://github.com/PacketParker/messagearr" target="_blank" referrerpolicy="no-referrer" class="slider__button">Source Code</a>
<a href="https://hub.docker.com/r/packetparker/messagearr" target="_blank" referrerpolicy="no-referrer" class="slider__button">Docker Hub</a>
</p>
</div>
</div>
</div> </div>
</section>
<footer><a title="My PGP Public Key" href="/static/contact.asc">PGP Public Key</a>&emsp;&emsp;Fingerprint: 58B7 6B8B BAB8 794D 21E2 579C 95CD 2E0C 7E32 9F2A</footer> <div class="navbar-right">
<a id="end"></a> <a href="/about">About</a>
<a href="/contact">Contact</a>
</div>
</nav>
<div class="container">
<h1>Hello, I'm Parker</h1>
<p>A student with a strong passion for technology and privacy.</p>
<a href="mailto:contact@pkrm.dev" target="_blank"><img class="icons" src="{{ url_for('static', filename='mail.png') }}"></a>
<a href="https://github.com/packetparker" target="_blank"><img class="icons" src="{{ url_for('static', filename='github.png') }}"></a>
<!-- <a href="https://linkedin.com" target="_blank"><img class="icons" src="{{ url_for('static', filename='linkedin.png') }}"></a> -->
<a href="/pgp" target="_blank"><img class="icons" src="{{ url_for('static', filename='gnupg.png') }}"></a>
</div>
<footer>PGP Fingerprint: 58B7 6B8B BAB8 794D 21E2 579C 95CD 2E0C 7E32 9F2A</footer>
</body> </body>
</html> </html>
<script src="https://cdn.jsdelivr.net/npm/@mojs/core"></script> <style>
/* Import Montserrat font */
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@200;300;400;500;600;700&display=swap');
body {
margin: 0;
padding: 0;
font-family: 'Montserrat', sans-serif;
background-color: #242528;
color: #9F9FAA;
}
/* Navbar styles */
nav {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
background-color: #212223;
color: #9F9FAA;
}
nav a {
text-decoration: none;
color: #9F9FAA;
font-size: 1.5rem;
margin: 0 1rem;
}
nav a:hover {
color: #fff;
}
.navbar-left {
display: flex;
justify-content: flex-start;
align-items: center;
margin-left: 15%;
}
.navbar-right {
display: flex;
justify-content: flex-end;
align-items: center;
margin-right: 15%;
}
/* Navbar media query for mobile */
@media only screen and (max-width: 600px) {
nav a {
margin: 0;
font-size: 1rem;
}
.navbar-left {
margin: 0 1rem;
}
.navbar-right {
margin: 0 1rem;
}
.navbar-right a {
margin-left: 1rem;
}
}
/* Container styles */
.container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 1.75rem;
text-align: center;
width: 500px;
}
.container h1 {
margin-bottom: 0;
}
.container p {
margin: 2rem 0 3rem 0;
}
.icons {
margin: 0 1rem;
height: 50px;
width: 50px;
color: #9F9FAA;
}
.icons:hover {
cursor: pointer;
opacity: 0.6;
transition: opacity 0.25s ease-in-out;
}
/* Continer media query for mobile */
@media only screen and (max-width: 600px) {
.container {
font-size: 1.5rem;
width: 300px;
}
.container p {
margin: 1rem 0 2rem 0;
}
.icons {
height: 40px;
width: 40px;
}
}
/* Footer styles */
footer {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
font-size: 1rem;
text-align: center;
margin-bottom: 2rem;
width: 90vw;
}
</style>

169
app/templates/pgp.html Normal file
View File

@ -0,0 +1,169 @@
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>Home</title>
</head>
<body>
<nav>
<div class="navbar-left">
<a href="/">~$&nbsp;&nbsp;&nbsp;cd /home</a>
</div>
<div class="navbar-right">
<a href="/about">About</a>
<a href="/contact">Contact</a>
</div>
</nav>
<div class="container">
<h1>Copy my key below, or click <a href="/parker.asc" style="text-decoration: underline; color: #9F9FAA;">here</a> to download it.</h1>
<p>
-----BEGIN PGP PUBLIC KEY BLOCK-----
<br><br>
xjMEZI06YRYJKwYBBAHaRw8BAQdAxymP7jguJxjtKKqGQ/fSXGwZVzOiix6iRS1F
6BVMu+3NG1BhcmtlciBNIDxjb250YWN0QHBrcm0uZGV2PsKTBBMWCgA7FiEEWLdr
i7q4eU0h4leclc0uDH4ynyoFAmSNOmECGwMFCwkIBwICIgIGFQoJCAsCBBYCAwEC
HgcCF4AACgkQlc0uDH4ynyrBIAEAu7y2oebvsBb+tJXPUOZkjqE+rsAwiDgHLj3U
gYMM4XUBALPsZ4IgA5mWhTDTV00QLHmVtybCJzCdc7LzyvQH78cHzjgEZI06YRIK
KwYBBAGXVQEFAQEHQLBpSX+qSOtSFVrp9+VfJGHsoPaIhoxIjz44byrtLXZ4AwEI
B8J4BBgWCgAgFiEEWLdri7q4eU0h4leclc0uDH4ynyoFAmSNOmECGwwACgkQlc0u
DH4ynyqQhAEAuo1HGXEKkBUzji+cCW3wF/oqg0cQklQzfKUkifLhiC8A/2gdilxS
AYHKY0lEJandkOjid/otDdiIZCUBt5mXjncL
=ni8o
<br>
-----END PGP PUBLIC KEY BLOCK-----
</p>
</div>
<footer>PGP Fingerprint: 58B7 6B8B BAB8 794D 21E2 579C 95CD 2E0C 7E32 9F2A</footer>
</body>
</html>
<style>
/* Import Montserrat font */
@import url('https://fonts.googleapis.com/css2?family=Montserrat:wght@200;300;400;500;600;700&display=swap');
body {
margin: 0;
padding: 0;
font-family: 'Montserrat', sans-serif;
background-color: #242528;
color: #9F9FAA;
}
/* Navbar styles */
nav {
display: flex;
justify-content: space-between;
align-items: center;
padding: 1rem 2rem;
background-color: #212223;
color: #9F9FAA;
}
nav a {
text-decoration: none;
color: #9F9FAA;
font-size: 1.5rem;
margin: 0 1rem;
}
nav a:hover {
color: #fff;
}
.navbar-left {
display: flex;
justify-content: flex-start;
align-items: center;
margin-left: 15%;
}
.navbar-right {
display: flex;
justify-content: flex-end;
align-items: center;
margin-right: 15%;
}
/* Navbar media query for mobile */
@media only screen and (max-width: 600px) {
nav a {
margin: 0;
font-size: 1rem;
}
.navbar-left {
margin: 0 1rem;
}
.navbar-right {
margin: 0 1rem;
}
.navbar-right a {
margin-left: 1rem;
}
}
/* Container styles */
.container {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
font-size: 1.25rem;
text-align: center;
width: 600px;
word-break: break-all;
}
.container p {
text-align: left;
}
/* Continer media query for mobile */
@media only screen and (max-width: 600px) {
.container {
font-size: 1.25rem;
width: 300px;
}
.container p {
margin: 1rem 0 2rem 0;
}
}
/* Footer styles */
footer {
position: absolute;
bottom: 0;
left: 50%;
transform: translateX(-50%);
font-size: 1rem;
text-align: center;
margin-bottom: 2rem;
width: 90vw;
}
</style>
<script>
// Get the h1
const h1 = document.querySelector('h1');
// Get the p tag
const p = document.querySelector('p');
// If the user is on mobile, remove the p tag and change the h1
if (window.innerWidth <= 600) {
p.remove();
h1.innerHTML = 'Click <a href="/parker.asc" style="text-decoration: underline; color: #9F9FAA;">here</a> to download my PGP key.';
h1.style.wordBreak = 'normal';
}
</script>

View File

@ -4,7 +4,6 @@ import discord
import dotenv import dotenv
app = flask.Flask(__name__) app = flask.Flask(__name__)
app.secret_key = os.urandom(32)
dotenv.load_dotenv() dotenv.load_dotenv()
webhook_url = os.getenv("WEBHOOK_URL") webhook_url = os.getenv("WEBHOOK_URL")
@ -12,6 +11,10 @@ webhook_url = os.getenv("WEBHOOK_URL")
def index(): def index():
return flask.render_template('index.html') return flask.render_template('index.html')
@app.route('/about', methods=['GET'])
def about():
return flask.render_template('about.html')
@app.route('/contact', methods=['GET', 'POST']) @app.route('/contact', methods=['GET', 'POST'])
def contact(): def contact():
if flask.request.method == 'GET': if flask.request.method == 'GET':
@ -35,4 +38,13 @@ def contact():
return flask.render_template('contact.html', success=True) return flask.render_template('contact.html', success=True)
# If any error happens for any reason, return the contact page with error # If any error happens for any reason, return the contact page with error
except: except:
return flask.render_template('contact.html', error=True) return flask.render_template('contact.html', error=True)
@app.route('/pgp', methods=['GET'])
def pgp():
return flask.render_template('pgp.html')
@app.route('/parker.asc', methods=['GET'])
def parker():
# Send the file to download
return flask.send_file('static/parker.asc', as_attachment=True)