This commit is contained in:
metacryst
2025-09-12 21:09:41 -05:00
parent eff0c160a5
commit ffb4d1ef52
11 changed files with 214 additions and 735 deletions

View File

@@ -1,3 +1,3 @@
BASE_URL=localhost:3004
BASE_URL=localhost:3003
JWT_SECRET=950b15c8c1c8a27dd716bba3ab51d96ce49afa85cae72884cf22e936e1bc0cb9
ENV=development

View File

@@ -12,7 +12,7 @@ var ENV string
// URLs
var BASE_URL string
const PORT = "3004"
const PORT = "3003"
// Auth
var JWT_SECRET string

View File

@@ -32,14 +32,6 @@ body {
color: var(--green);
}
a {
text-decoration: none;
color: var(--green);
transition: background .2s, padding .2s, color .2s;
padding: 4px;
border-radius: 5px;
}
button {
background-color: var(--green);
color: var(--tan);

View File

@@ -1,19 +0,0 @@
css(`
nav-bar {
position: fixed;
top: 0px;
left: 0px;
}
`)
export default class NavBar extends HTMLElement {
connectedCallback() {
this.innerHTML += /* html */ `
`
}
}
customElements.define("nav-bar", NavBar)

View File

@@ -1,581 +0,0 @@
css(`
questionnaire- {
display: grid;
grid-template-columns: 250px 1fr;
gap: 20px;
max-width: 1200px;
margin: 0 auto;
padding: 20px;
margin-top: 17vh;
}
.toc {
position: sticky;
top: 17vh;
align-self: start;
background: var(--tan);
padding: 10px;
border: 1px solid var(--brown);
border-radius: 5px;
}
.toc h2 {
margin-top: 0;
font-size: 1.1em;
}
.toc a {
display: block;
margin: 5px 0;
text-decoration: none;
color: var(--green);
}
.toc a.active {
font-weight: bold;
color: var(--red);
}
section {
border: 1px solid var(--brown);
margin-bottom: 2vh;
padding: 15px;
border-radius: 5px;
background: none;
user-select: none;
transition: background 0.2s, border 0.2s, color 0.2s;
}
.section-content {
scroll-margin-top: 20px;
padding-bottom: 40px;
border-bottom: 1px solid #ccc;
}
input:focus,
textarea:focus,
select:focus {
outline: 1px solid #ddbb36;
border: 1px solid #c4a52f
}
/* Normal state */
form button {
transition: all 0.3s ease;
padding: 8px 16px;
border-radius: 4px;
}
/* Loading: morph into circle + pulse */
form button.loading {
width: 40px;
height: 40px;
border-radius: 50%;
padding: 0;
display: flex;
justify-content: center;
align-items: center;
animation: pulse 1s infinite;
}
/* Success state */
button.success {
background: #28a745;
color: white;
border-radius: 4px;
width: auto;
animation: none;
}
@keyframes pulse {
0% { transform: scale(1); opacity: 1; }
50% { transform: scale(1.1); opacity: 0.7; }
100% { transform: scale(1); opacity: 1; }
}
`)
export default class Questionnaire extends HTMLElement {
constructor() {
super();
this.pages = [
{ title: "Demographic Information", slug: "demographic", content: this.demographicContent() },
{ title: "Contact Information", slug: "contact", content: this.contactContent() },
{ title: "Personal History", slug: "personal-history", content: this.personalHistoryContent() },
{ title: "Ancestry", slug: "ancestry", content: this.ancestryContent() },
{ title: "Skills", slug: "skills", content: this.skillsContent() },
{ title: "Interest", slug: "interest", content: this.interestContent() },
{ title: "Criminal Record", slug: "criminal", content: this.criminalContent() },
{ title: "Political Perspectives", slug: "political", content: this.politicalContent() },
{ title: "Religious/Philosophical Views", slug: "religious", content: this.religiousContent() },
{ title: "Federal Employment", slug: "federal-employment", content: this.federalEmploymentContent() },
{ title: "Social Perspectives", slug: "social", content: this.socialContent() },
];
this.pageHTML = {};
}
connectedCallback() {
this.fetchData();
}
async fetchData() {
try {
const res = await fetch("/api/get-application");
if (!res.ok) throw new Error(`HTTP error ${res.status}`);
this.data = await res.json();
console.log(this.data)
} catch (err) {
console.error("Failed to fetch questionnaire:", err);
this.data = {};
}
this.render()
}
render() {
this.Layout();
this.Forms();
this.setupScrollSpy();
this.populateFormData();
this.markSectionsComplete();
}
Layout() {
this.innerHTML = /* html */`
<nav class="toc">
<h2>Contents</h2>
${this.pages.map(p => `
<a href="#${p.slug}" data-slug="${p.slug}">${p.title}</a>
`).join("")}
<a href="#submit">Submit</a>
</nav>
<div class="sections">
${this.pages.map(p => /* html */`
<section id="${p.slug}" class="section-content">
<h1 style="margin-left: 3rem; margin-top: 3rem">${p.title}</h1>
<div class="form-container">
<form></form>
</div>
</section>
`).join("")}
<section id="submit" class="section-content">
<h1 style="margin-left: 3rem; margin-top: 3rem">Submit Application</h1>
<button style="margin-left: 3rem; margin-top: 3rem; background-color: gray" disabled>Submit</button>
</section>
</div>
`;
}
Forms() {
this.pages.forEach(page => {
const form = this.querySelector(`#${page.slug} form`);
if (form) form.innerHTML = page.content;
form.innerHTML += `<button type="submit">Save</button>`
form.addEventListener("submit", this.onFormSubmit)
});
}
populateFormData() {
Object.entries(this.data).forEach(([key, value]) => {
const fields = document.querySelectorAll(`[name="${key}"]`);
if (!fields.length) return;
fields.forEach(field => {
if (field.type === "checkbox" || field.type === "radio") {
if (Array.isArray(value)) {
field.checked = value.includes(field.value);
} else {
field.checked = field.value === value;
}
} else if (field.tagName === "SELECT" && field.multiple && Array.isArray(value)) {
Array.from(field.options).forEach(opt => {
opt.selected = value.includes(opt.value);
});
} else {
field.value = value;
}
});
});
}
markSectionsComplete() {
let allFormsComplete = true
let forms = document.querySelectorAll("form")
for(let i =0; i<forms.length; i++) {
let form = forms[i]
if(form.checkValidity()) {
form.closest("section").style.backgroundColor = "#0080003d"
let toc = document.querySelector(`[data-slug=${form.closest("section").id}]`)
if(!toc.innerHTML.includes("✓"))
toc.innerHTML += " ✓"
} else {
allFormsComplete = false
}
}
if(allFormsComplete) {
let button = document.querySelector("section#submit button")
button.style.backgroundColor = ""
button.removeAttribute("disabled")
button.addEventListener("click", () => window.location.href = "/complete")
}
}
setupScrollSpy() {
const tocLinks = this.querySelectorAll(".toc a");
const sections = this.querySelectorAll(".section-content");
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
const id = entry.target.id;
history.replaceState(null, "", `#${id}`);
tocLinks.forEach(link => {
link.classList.toggle("active", link.dataset.slug === id);
});
}
});
}, {
rootMargin: "-50% 0px -50% 0px", // middle of screen
threshold: 0
});
sections.forEach(section => observer.observe(section));
}
onFormSubmit = (e) => {
e.preventDefault();
let form = e.target
let button = form.querySelector('button[type="submit"]');
if (!form.checkValidity()) {
// Let the browser display native validation errors
return;
}
const showButtonLoading = () => {
button.classList.add('loading');
button.disabled = true;
button.textContent = '';
}
const showButtonError = () => {
button.classList.remove('loading');
button.disabled = false;
button.textContent = 'Save';
if(!form.querySelector("p .error")) {
form.parentElement.appendChild(html(`<p class="error" style="color: red">There has been an error. Please try again.</p>`))
}
}
const showButtonSuccess = () => {
if(form.parentElement.querySelector("p")) {
form.parentElement.querySelector("p").remove()
}
button.classList.remove('loading');
button.classList.add('success');
button.textContent = 'Success!';
console.log(this)
this.markSectionsComplete()
}
showButtonLoading()
// Collect form data
const data = new FormData(form);
const values = {};
data.forEach((v, k) => {
if (values[k]) {
// Already an array? push new value
if (Array.isArray(values[k])) {
values[k].push(v);
} else {
values[k] = [values[k], v];
}
} else {
values[k] = v;
}
});
fetch('/api/application-save', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify(values)
})
.then(res => {
if (!res.ok) {
throw new Error(`Server error: ${res.status}`);
}
return res.text();
})
.then(responseData => {
console.log(form, form.parentElement)
showButtonSuccess()
console.log('Server response:', responseData);
})
.catch(err => {
showButtonError()
console.error('Submission error:', err);
});
console.log("Form submitted:", values);
}
demographicContent() {
return /* html */`
<label for="firstName">First Name *</label>
<input type="text" name="firstName" class="form-control" required>
<br><br>
<label for="lastName">Last Name *</label>
<input type="text" name="lastName" class="form-control" required>
<br><br>
<label for="age">Age *</label>
<input type="text" name="age" class="form-control" required>
<br><br>
<label for="gender">Gender *</label>
<select name="gender" class="form-control" required>
<option value="">Select...</option>
<option value="male">Male</option>
<option value="female">Female</option>
</select>
<br><br>
<label for="ethnicity">Type of Application *</label>
<select name="applicationType" class="form-control" required>
<option value="">Select...</option>
<option value="single">Single</option>
<option value="couple">Couple</option>
<option value="family">Family</option>
</select>
<br><br>
<label for="sexualOrientation">Sexual Orientation *</label>
<select name="sexualOrientation" class="form-control" required>
<option value="">Select...</option>
<option value="straight">Straight</option>
<option value="gay">Gay</option>
<option value="bisexual">Bisexual</option>
<option value="other">Other</option>
</select>
<br><br>
`;
}
contactContent() {
return /* html */`
<div class="form-group">
<label for="telegram">Telegram Username *</label>
<input type="text" name="telegram" class="form-control" required>
<small style="display:block;margin-top:.25rem;color:#666;">A Telegram account is required to be interviewed by the PMA</small>
</div>
<div class="form-group">
<label for="email-2">Secondary Email</label>
<input type="email" name="email-2" class="form-control">
<small style="display:block;margin-top:.25rem;color:#666;">Not required - you may add a secondary email in case our messages don't reach you</small>
</div>
<div class="form-group">
<label for="phone">Phone Number</label>
<input type="tel" name="phone" class="form-control">
<small style="display:block;margin-top:.25rem;color:#666;">Not required, but recommended if you don't provide a Telegram handle.</small>
</div>
`;
}
personalHistoryContent() {
return /* html */`
<div class="form-group">
<label for="personalHistory">Where did you come from, where did you grow up, and where do you live now? *</label>
<textarea name="personalHistory" class="form-control" required minlength="10"></textarea>
</div>
`;
}
ancestryContent() {
return /* html */`
<div class="form-group">
<p>Please select all that apply to your ancestry/heritage:</p>
<div class="ancestry-grid">
<label class="ancestry-option"><input type="checkbox" name="ancestry" value="english"> English</label>
<label class="ancestry-option"><input type="checkbox" name="ancestry" value="irish"> Irish</label>
<label class="ancestry-option"><input type="checkbox" name="ancestry" value="scottish"> Scottish</label>
<label class="ancestry-option"><input type="checkbox" name="ancestry" value="welsh"> Welsh</label>
<label class="ancestry-option"><input type="checkbox" name="ancestry" value="german"> German</label>
<label class="ancestry-option"><input type="checkbox" name="ancestry" value="french"> French</label>
<label class="ancestry-option"><input type="checkbox" name="ancestry" value="dutch"> Dutch</label>
<label class="ancestry-option"><input type="checkbox" name="ancestry" value="scandinavian"> Scandinavian</label>
<label class="ancestry-option"><input type="checkbox" name="ancestry" value="slavic"> Slavic</label>
<label class="ancestry-option"><input type="checkbox" name="ancestry" value="balkan"> Balkan</label>
<label class="ancestry-option"><input type="checkbox" name="ancestry" value="iberian"> Iberian</label>
<label class="ancestry-option"><input type="checkbox" name="ancestry" value="other">Other</label>
</div>
</div>
<div class="form-group">
<label for="ancestryDetails">Tell us more about your ancestry and family history *</label>
<textarea name="ancestryDetails" class="form-control" required minlength="20" placeholder="Please provide details about your family background, heritage, and any ancestral connections you're aware of."></textarea>
</div>
`;
}
skillsContent() {
return /* html */`
<div class="form-group">
<label for="skills">Describe any special skills or trades you have. *</label>
<textarea name="skills" class="form-control" required minlength="50" placeholder="e.g., carpentry, plumbing, electrical, masonry, welding"></textarea>
</div>
`;
}
interestContent() {
return /* html */`
<div class="form-group">
<label for="interest">Why are you interested in Return To The Land, and how did you find out about us? *</label>
<textarea name="interest" class="form-control" required minlength="20"></textarea>
</div>
`;
}
criminalContent() {
return /* html */`
<label for="criminal">Do you have a criminal record? If so, please explain. Otherwise, just say "no". *</label>
<textarea name="criminal" class="form-control" required></textarea>
<br><br>
`;
}
politicalContent() {
return /* html */`
<label for="politicsNotes">Give a quick summary of your political ideas. *</label>
<textarea name="politicsNotes" class="form-control" style="width:100%;min-height:120px" required minlength="40"></textarea>
<br><br>
`;
}
religiousContent() {
return /* html */`
<div class="form-group">
<label>Do you identify with a particular religion? *</label>
<div class="radio-row">
<label><input type="radio" name="religionIdent" value="yes" required> Yes</label>
<label><input type="radio" name="religionIdent" value="no"> No</label>
</div>
</div>
<div class="form-group">
<label for="religionName">If yes, which?</label>
<input name="religionName" class="form-control" placeholder="e.g., Catholic, Orthodox, Protestant, Deist, Other">
</div>
<div class="form-group">
<label>Do you regularly attend services or observances?</label>
<div class="radio-row">
<label><input type="radio" name="attendance" value="weekly"> Weekly</label>
<label><input type="radio" name="attendance" value="monthly"> Monthly</label>
<label><input type="radio" name="attendance" value="occasionally"> Occasionally</label>
<label><input type="radio" name="attendance" value="never"> Never</label>
</div>
</div>
<div class="form-group">
<label for="philosophy">Briefly describe your moral or philosophical outlook *</label>
<textarea name="philosophy" class="form-control" required></textarea>
</div>
`;
}
federalEmploymentContent() {
return /* html */`
<div class="form-group">
<label>Are you currently employed by, or otherwise receive funds to perform work for, the United States federal government or a contractor/affiliate/partner? *</label>
<div class="radio-row">
<label><input type="radio" name="employedByGovernment" value="yes" required> Yes</label>
<label><input type="radio" name="employedByGovernment" value="no"> No</label>
</div>
</div>
`;
}
socialContent() {
return /* html */`
<p class="note">Please answer these questions regarding controversial topics. Anything that needs further explanation can be done in the interview.</p>
<br>
<div class="form-group">
<label>I support gay marriage *</label>
<div class="radio-row">
<label><input type="radio" name="gays" value="yes" required> Yes</label>
<label><input type="radio" name="gays" value="no"> No</label>
<label><input type="radio" name="gays" value="neutral"> Neutral</label>
</div>
</div>
<div class="form-group">
<label>I support transgenderism *</label>
<div class="radio-row">
<label><input type="radio" name="trannies" value="yes" required> Yes</label>
<label><input type="radio" name="trannies" value="no"> No</label>
<label><input type="radio" name="trannies" value="neutral"> Neutral</label>
</div>
</div>
<div class="form-group">
<label>I support foreign immigration *</label>
<div class="radio-row">
<label><input type="radio" name="immigration" value="yes" required> Yes</label>
<label><input type="radio" name="immigration" value="no"> No</label>
<label><input type="radio" name="immigration" value="neutral"> Neutral</label>
</div>
</div>
<div class="form-group">
<label>I support the COVID vaccine and mask *</label>
<div class="radio-row">
<label><input type="radio" name="vax" value="yes" required> Yes</label>
<label><input type="radio" name="vax" value="no"> No</label>
<label><input type="radio" name="vax" value="neutral"> Neutral</label>
</div>
</div>
<div class="form-group">
<label>I support abortion *</label>
<div class="form-group">
<label><input type="radio" name="abortion" value="yes" required> Yes</label>
<label><input type="radio" name="abortion" value="no"> No</label>
<label><input type="radio" name="abortion" value="neutral"> Neutral</label>
</div>
</div>
<div class="form-group">
<label>I support segregation *</label>
<div class="radio-row">
<label><input type="radio" name="segregation" value="yes" required> Yes</label>
<label><input type="radio" name="segregation" value="no"> No</label>
<label><input type="radio" name="segregation" value="neutral"> Neutral</label>
</div>
</div>
<div class="form-group">
<label>I support procreation *</label>
<div class="radio-row">
<label><input type="radio" name="procreation" value="yes" required> Yes</label>
<label><input type="radio" name="procreation" value="no"> No</label>
<label><input type="radio" name="procreation" value="neutral"> Neutral</label>
</div>
</div>
<div class="form-group">
<label>Multiculturalism preference *</label>
<div class="radio-row">
<label style="display:block;margin:.35rem 0"><input type="radio" name="multiculturalism" value="yes" required> I prefer a community with a variety of different ancestral origins</label>
<label style="display:block;margin:.35rem 0"><input type="radio" name="multiculturalism" value="no"> I prefer a community where everyone shares common continental ancestry</label>
<label style="display:block;margin:.35rem 0"><input type="radio" name="multiculturalism" value="neutral"> Neutral</label>
</div>
</div>
<div class="form-group">
<label>How often do you think about the Roman empire? *</label>
<div class="radio-row">
<label style="display:block;margin:.35rem 0"><input type="radio" name="romanEmpire" value="daily" required> Every day, at least once</label>
<label style="display:block;margin:.35rem 0"><input type="radio" name="romanEmpire" value="weekly"> A few times a week, probably</label>
<label style="display:block;margin:.35rem 0"><input type="radio" name="romanEmpire" value="rarely"> Very rarely</label>
<label style="display:block;margin:.35rem 0"><input type="radio" name="romanEmpire" value="never"> Never</label>
</div>
</div>
`;
}
}
customElements.define("questionnaire-", Questionnaire);

View File

@@ -1,97 +0,0 @@
:root {
--bar-bg: #ccc;
--bar-fill: var(--green, #4caf50);
}
.progress-container {
position: absolute;
top: 20vh;
left: 0;
width: 100%;
background: var(--green);
height: 10px;
}
.content {
margin-top: calc(20vh + 30px);
display: grid;
grid-template-columns: repeat(3, minmax(250px, 1fr));
gap: 20px;
max-width: 90vw;
margin-left: auto;
margin-right: auto;
}
.section:hover {
background: var(--red);
color: var(--tan);
}
.section:hover .status {
color: var(--tan);
}
.section h2 {
margin: 0 0 10px;
}
.status {
font-weight: bold;
color: red;
}
/* Questionnaire Pages */
.form-container {
max-width: 800px;
margin: 2rem auto;
padding: 2rem;
/* background: rgba(255, 255, 255, 0.9); */
}
.form-container input {
background: var(--tan);
border: 1px solid var(--brown);
}
.form-container select {
background: var(--tan);
border: 1px solid var(--brown);
}
.form-group { margin-bottom: 1.5rem; }
label { display: block; margin-bottom: 0.5rem; font-weight: bold; }
.form-control {
width: 100%;
padding: 0.5rem;
border-radius: 4px;
}
textarea {
background: var(--tan);
border: 1px solid var(--brown)
}
textarea.form-control { min-height: 120px; resize: vertical; }
.btn {
background-color: var(--green);
color: #fff;
padding: 0.5rem 1rem;
border: none;
border-radius: 4px;
cursor: pointer;
}
.btn:hover { background-color: var(--dark-green); }
.btn-outline { background: none; border: 1px solid var(--green); color: var(--green); }
.radio-row { margin: 0.25rem 0; }
.note { color: #444; margin: 0.5rem 0 0 0; }
/* Grid/option helpers used by ancestry */
.ancestry-grid { display: grid; grid-template-columns: repeat(auto-fill, minmax(200px, 1fr)); gap: 1rem; margin-bottom: 1.5rem; }
.ancestry-option { border: 1px solid var(--brown); padding: 1rem; border-radius: 4px; cursor: pointer; transition: all 0.2s; }
.ancestry-option:hover { border-color: var(--green); background-color: rgba(76, 175, 80, 0.1); }
.ancestry-option input[type="checkbox"] { margin-right: 0.5rem; }

View File

@@ -1,23 +0,0 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hyperia | Application</title>
<link rel="stylesheet" href="_/code/shared.css">
<link rel="icon" href="_/icons/logo.svg">
<script src="_/code/util.js"></script>
<link rel="stylesheet" href="index.css">
<script type="module">
import NavBar from "./components/NavBar.js"
import Questionnaire from "./components/Questionnaire.js"
</script>
</head>
<body>
<nav-bar></nav-bar>
<questionnaire-></questionnaire->
<div style="position: fixed; bottom: 2vh; left: 1vw">
</div>
</body>
</html>

View File

@@ -16,7 +16,7 @@
#items {
position: absolute;
top: 45vh;
top: 47vh;
left: 50vw;
transform: translate(-50%, -50%);
@@ -36,6 +36,7 @@
text-underline-offset: 5px;
transition: background .02s, color .2s;
user-select: none;
color: black;
display: inline-block; /* makes background and padding behave */
padding: 0.2em 0.5em; /* adds breathing room */
@@ -51,6 +52,12 @@
background: var(--red); /* background color works now */
color: white; /* optional: change text color for contrast */
}
@media (max-width: 600px) {
#title {
font-size: 30vw
}
}
</style>
<script src="_/code/util.js"></script>
<script type="module">
@@ -66,11 +73,12 @@
<img src="_/icons/left_plant.svg" style="position: absolute; left: 25%; top: 23vh; width: 70%; transform: translateX(-50%)">
<img src="_/icons/right_plant.svg" style="position: absolute; left: 75%; top: 23vh; width: 70%; transform: translateX(-50%)">
<div style="height: 2vh"></div>
<span style="font-family: Canterbury; color: black; font-size: 5vh;">A <br>Society</span>
<span style="font-family: Canterbury; color: black; font-size: 4.5vh;">A <br>Classical <br> Christian <br> Society</span>
<div style="height: 2vh"></div>
<div style="color: black; font-size: 2.2vh; z-index: 1; cursor: default;">
<a>ABOUT</a> | <a>SERVICES</a> | <a>JOIN</a>
<div style="color: black; font-size: 2vh; z-index: 1; cursor: default;">
<a href="signin">SIGN IN</a>
<a href="join">JOIN</a>
</div>
</div>
</body>
</html>
</html>

77
ui/public/pages/join.html Normal file
View File

@@ -0,0 +1,77 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hyperia</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="_/icons/logo.svg">
<link rel="stylesheet" href="_/code/shared.css">
<link rel="stylesheet" href="index.css">
<style>
:root {
--green: #0B5538;
--tan: #FFDFB4;
--red: #BC1C02;
--brown: #c6a476
}
#items {
position: absolute;
top: 45vh;
left: 50vw;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center; /* centers children horizontally */
text-align: center; /* ensures text inside spans is centered */
}
#title {
font-size: 10vh;
position: absolute;
left: 2vw;
top: 2vh;
}
a {
cursor: default;
text-decoration: underline;
text-underline-offset: 5px;
transition: background .02s, color .2s;
user-select: none;
padding: 4px;
border-radius: 5px;
display: inline-block; /* makes background and padding behave */
padding: 0.2em 0.5em; /* adds breathing room */
}
a:hover {
text-decoration: none;
background: var(--green);
color: var(--tan);
}
a:active {
background: var(--red); /* background color works now */
color: white; /* optional: change text color for contrast */
}
</style>
<script src="_/code/util.js"></script>
<script type="module">
import PageFooter from "./components/Footer.js"
import NavBar from "./components/NavBar.js"
import SideBar from "./components/SideBar.js"
</script>
</head>
<body>
<span id="title" style="font-family: Canterbury; color: var(--red); margin-bottom: 10vh">hyperia</span>
<div id="items">
</div>
<script>
Array.from($("a*")).forEach((link) => {
link.addEventListener("click", () => window.history.pushState("", "", link.innerHTML.toLowerCase()))
})
</script>
</body>
</html>

122
ui/public/pages/signin.html Normal file
View File

@@ -0,0 +1,122 @@
<!DOCTYPE html>
<html lang="en">
<head>
<title>Hyperia</title>
<meta name="viewport" content="width=device-width, initial-scale=1">
<link rel="icon" href="_/icons/logo.svg">
<link rel="stylesheet" href="_/code/shared.css">
<link rel="stylesheet" href="index.css">
<style>
:root {
--green: #0B5538;
--tan: #FFDFB4;
--red: #BC1C02;
--brown: #c6a476
}
#items {
position: absolute;
top: 45vh;
left: 50vw;
transform: translate(-50%, -50%);
display: flex;
flex-direction: column;
align-items: center; /* centers children horizontally */
text-align: center; /* ensures text inside spans is centered */
}
#title {
font-size: 10vh;
position: absolute;
left: 2vw;
top: 2vh;
}
a {
cursor: default;
text-decoration: underline;
text-underline-offset: 5px;
transition: background .02s, color .2s;
user-select: none;
padding: 4px;
border-radius: 5px;
display: inline-block; /* makes background and padding behave */
padding: 0.2em 0.5em; /* adds breathing room */
}
a:hover {
text-decoration: none;
background: var(--green);
color: var(--tan);
}
a:active {
background: var(--red); /* background color works now */
color: white; /* optional: change text color for contrast */
}
input {
background-color: transparent;
border: none;
border-radius: 5px;
border-top: 2px solid var(--green);
border-bottom: 2px solid var(--green);
height: 2em;
width: 15vw;
padding: 0.2em;
transition: border .2s, padding .2s;
}
input:focus {
outline: none;
padding: 0.4em;
border-top: 2px solid var(--red);
border-bottom: 2px solid var(--red);
}
input:focus::placeholder {
color: var(--red)
}
input::placeholder {
color: var(--green);
}
input:-webkit-autofill,
input:-webkit-autofill:focus {
transition: background-color 600000s 0s, color 600000s 0s;
}
input[data-autocompleted] {
background-color: #c7a67b !important;
}
@media (max-width: 600px) {
input {
width: 50vw
}
}
</style>
<script src="_/code/util.js"></script>
<script type="module">
import PageFooter from "./components/Footer.js"
import NavBar from "./components/NavBar.js"
import SideBar from "./components/SideBar.js"
</script>
</head>
<body>
<span onclick="window.location = '/'"id="title" style="cursor: default; font-family: Canterbury; color: var(--red); margin-bottom: 10vh">hyperia</span>
<div id="items">
<input placeholder="email"></input>
<br>
<input placeholder="password"></input>
</div>
<script>
Array.from($("a*")).forEach((link) => {
link.addEventListener("click", () => window.history.pushState("", "", link.innerHTML.toLowerCase()))
})
</script>
</body>
</html>