add more color modes, respect dark mode setting, use button for among

Signed-off-by: Naman Sood <mail@nsood.in>
This commit is contained in:
Naman Sood 2023-02-11 17:33:00 -05:00
parent 7f3de55234
commit 294e147d39
5 changed files with 196 additions and 175 deletions

View file

@ -5,7 +5,7 @@ This is the code for my personal website and resume. The main branch of this rep
## Cool things that I never get to brag about
* The header section of the website always correctly indicates my age, as long as you have JavaScript enabled. It will calculate and display my age based on my most recent birthday.
* There is a dark mode in this website, accessible by typing `clapoff`. Light mode can be restored by typing `clapon`.
* There is a dark mode in this website, accessible by typing `dark`. Light mode can be restored by typing `lite`. Other color modes are also available -- `purp`, `yell`, `blue`, `mint` at least. See `js/script.js` for an exhaustive list. Your preference can be stored permanently by typing `save`.
* I spent a decent amount of time worrying about accessibility in the later iterations of this website, so hopefully that should not be an issue for anyone. If there's anything I can do to improve on that front, please let me know.
## License

View file

@ -10,36 +10,59 @@ html {
body {
margin: 0;
font-family: "Nunito", sans-serif;
background: var(--bgColor);
}
@media (prefers-color-scheme: light) {
body {
--bgColor: white;
--textColor: #777;
--iconColor: black;
--boldColor: black;
}
}
@media (prefers-color-scheme: dark) {
body {
--bgColor: black;
--iconColor: white;
--textColor: #aaa;
--boldColor: white;
}
}
body.lite-mode {
--bgColor: white;
--textColor: #777;
--iconColor: black;
--boldColor: black;
}
body.dark-mode {
background: black;
color: white;
--bgColor: black;
--iconColor: white;
--textColor: #aaa;
--boldColor: white;
}
body.dark-mode .bg {
opacity: 0 !important;
body.mint-mode {
--bgColor: #c7fcee;
--iconColor: #486b61;
--textColor: #5f8c80;
--boldColor: #10352b;
}
body.dark-mode .main h1 {
color: #fff;
body.purp-mode {
--bgColor: #cfc7fc;
--iconColor: #5a518c;
--textColor: #7166af;
--boldColor: #312c4c;
}
body.dark-mode .main em {
color: #fff;
body.yell-mode {
--bgColor: #fcfcbf;
--iconColor: #54543f;
--textColor: #8c8c5f;
--boldColor: #353528;
}
body.dark-mode .main h3 {
color: #aaa;
}
body.dark-mode .main h3 a {
color: #fff;
}
body.dark-mode .main h3 a:before, body.dark-mode .main h3 a:after {
background: rgba(255, 255, 255, 0.2);
}
body.dark-mode .main h3 a.among {
color: inherit;
}
body.dark-mode .main a.contact-method p {
color: #fff;
font-size: 1rem;
body.blue-mode {
--bgColor: #ade3ff;
--iconColor: #394b54;
--textColor: #46748c;
--boldColor: #242f35;
}
.main {
@ -69,43 +92,35 @@ body.dark-mode .main a.contact-method p {
font-size: 4rem;
font-weight: 600;
margin: 0 0 2rem;
color: #000;
color: var(--boldColor);
}
.main h3 {
margin: 1rem 0 0;
font-size: 1.7rem;
line-height: 1.4;
font-weight: normal;
color: #777;
color: var(--textColor);
}
.main em {
color: #000;
color: var(--boldColor);
font: inherit;
}
.main h3 a {
color: #000;
color: var(--boldColor);
text-decoration: none;
border-bottom: 0.2rem solid transparent;
white-space: nowrap;
position: relative;
}
.main h3 a.among {
color: inherit;
border-bottom: 0;
cursor: text;
}
.main h3 a.among:before, .main h3 a.among:after {
content: none;
}
.main h3 a:before, .main h3 a:after {
content: "";
bottom: 0.4rem;
left: 0;
width: 100%;
height: 0.1rem;
background: rgba(51, 51, 51, 0.2);
background: var(--boldColor);
position: absolute;
opacity: 0.2;
opacity: 0.04;
transition: all 0.2s ease;
}
.main h3 a:before {
@ -113,11 +128,19 @@ body.dark-mode .main a.contact-method p {
transform-origin: center left;
}
.main h3 a:after {
opacity: 0.5;
opacity: 0.1;
}
.main h3 a:hover:before {
transform: scaleX(1);
opacity: 1;
opacity: 0.2;
}
.main h3 button {
color: inherit;
font: inherit;
margin: 0;
padding: 0;
border: 0;
background: none;
}
.main .contact-methods {
display: flex;
@ -132,6 +155,9 @@ body.dark-mode .main a.contact-method p {
width: 4rem;
height: 4rem;
}
.main .contact-methods a.contact-method i {
color: var(--iconColor);
}
.main .contact-methods a.contact-method p {
margin: 0;
position: absolute;
@ -219,6 +245,7 @@ body.dark-mode .main a.contact-method p {
pointer-events: none;
transition: none;
opacity: 0;
color: var(--boldColor);
}
.clapper.clapping {

View file

@ -10,45 +10,62 @@ html {
body {
margin: 0;
font-family: 'Nunito', sans-serif;
}
background: var(--bgColor);
body.dark-mode {
background: black;
color: white;
.bg {
opacity: 0 !important;
@media(prefers-color-scheme: light) {
--bgColor: white;
--textColor: #777;
--iconColor: black;
--boldColor: black;
}
.main {
h1 {
color: #fff;
}
@media(prefers-color-scheme: dark) {
--bgColor: black;
--iconColor: white;
--textColor: #aaa;
--boldColor: white;
}
em {
color: #fff;
}
&.lite-mode {
--bgColor: white;
--textColor: #777;
--iconColor: black;
--boldColor: black;
}
h3 {
color: #aaa;
}
&.dark-mode {
--bgColor: black;
--iconColor: white;
--textColor: #aaa;
--boldColor: white;
}
h3 a {
color: #fff;
&.mint-mode {
--bgColor: #c7fcee;
--iconColor: #486b61;
--textColor: #5f8c80;
--boldColor: #10352b;
}
&:before, &:after {
background: rgba(#fff, 0.2);
}
&.purp-mode {
--bgColor: #cfc7fc;
--iconColor: #5a518c;
--textColor: #7166af;
--boldColor: #312c4c;
}
&.among {
color: inherit;
}
}
&.yell-mode {
--bgColor: #fcfcbf;
--iconColor: #54543f;
--textColor: #8c8c5f;
--boldColor: #353528;
}
a.contact-method p {
color: #fff;
font-size: 1rem;
}
&.blue-mode {
--bgColor: #ade3ff;
--iconColor: #394b54;
--textColor: #46748c;
--boldColor: #242f35;
}
}
@ -83,7 +100,7 @@ body.dark-mode {
font-size: 4rem;
font-weight: 600;
margin: 0 0 2rem;
color: #000;
color: var(--boldColor);
}
h3 {
@ -91,39 +108,30 @@ body.dark-mode {
font-size: 1.7rem;
line-height: 1.4;
font-weight: normal;
color: #777;
color: var(--textColor);
}
em {
color: #000;
color: var(--boldColor);
font: inherit;
}
h3 a {
color: #000;
color: var(--boldColor);
text-decoration: none;
border-bottom: 0.2rem solid transparent;
white-space: nowrap;
position: relative;
&.among {
color: inherit;
border-bottom: 0;
&:before, &:after {
content: none;
}
cursor: text;
}
&:before, &:after {
content: '';
bottom: 0.4rem;
left: 0;
width: 100%;
height: 0.1rem;
background: rgba(#333, 0.2);
background: var(--boldColor);
position: absolute;
opacity: 0.2;
opacity: 0.04;
transition: all 0.2s ease;
}
@ -133,15 +141,24 @@ body.dark-mode {
}
&:after {
opacity: 0.5;
opacity: 0.1;
}
&:hover:before {
transform: scaleX(1);
opacity: 1;
opacity: 0.2;
}
}
h3 button {
color: inherit;
font: inherit;
margin: 0;
padding: 0;
border: 0;
background: none;
}
.contact-methods {
display: flex;
margin-left: -1.5rem;
@ -155,6 +172,10 @@ body.dark-mode {
width: 4rem;
height: 4rem;
i {
color: var(--iconColor);
}
p {
margin: 0;
position: absolute;
@ -258,6 +279,7 @@ body.dark-mode {
pointer-events: none;
transition: none;
opacity: 0;
color: var(--boldColor);
}
.clapper.clapping {

View file

@ -22,7 +22,7 @@
<canvas id="bg" alt=""></canvas>
<div class="text-container">
<h1>Hi, I&rsquo;m Naman.</h1>
<h3>I&rsquo;m a<span class="age"> twenty one</span> year-old who's fond of <em>coffee</em>, <em>math</em>, <em>writing</em>, <em>cars</em>, <a class="among" role="button" href="javascript:among()">among</a> other things. I also enjoy <em>programming</em> enough to do that for a living. I'm currently studying <em>computer science</em> at the <em>University of Waterloo</em>.</h3>
<h3>I&rsquo;m a<span class="age"> twenty one</span> year-old who's fond of <em>coffee</em>, <em>math</em>, <em>writing</em>, <em>cars</em>, <button class="among">among</button> other things. I also enjoy <em>programming</em> enough to do that for a living. I'm currently studying <em>computer science</em> at the <em>University of Waterloo</em>.</h3>
<h3>You can click to see my <a href="/resume.pdf">resume</a>, visit my <a href="https://prose.nsood.in">blog</a>, or check out ways to contact me below.</h3>
<div class="contact-methods">
<a href="mailto:mail@nsood.in" class="contact-method" aria-label="Email">

View file

@ -3,34 +3,30 @@ const canvas = document.querySelector('canvas#bg');
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
const snowing = false;
const ctx = canvas.getContext('2d');
const FILL_STYLES = {
light: 'rgba(0,0,0,0.05)',
dark: 'rgba(255,255,255,0.15)',
snowing: 'rgb(206, 212, 220)'
dark: 'rgba(255,255,255,0.15)'
};
const BACKGROUNDS = {
evening: 'linear-gradient(#011d32, #3b3b89)',
night: '#000000'
};
ctx.fillStyle = snowing ? FILL_STYLES.snowing : FILL_STYLES.light;
let darkMode = 0;
if(snowing) {
document.body.classList.add('dark-mode');
document.body.style.background = BACKGROUNDS.evening;
const colorMode = localStorage.getItem('color-mode');
if(colorMode) {
document.body.setAttribute('class', colorMode);
}
addEventListener('resize', () => {
canvas.width = window.innerWidth;
canvas.height = window.innerHeight;
// need to set ctx.fillStyle whenever I resize???
ctx.fillStyle = darkMode ? FILL_STYLES.dark : snowing ? FILL_STYLES.snowing : FILL_STYLES.light;
});
function isDarkMode() {
return document.body.classList.contains('dark-mode') ||
(document.body.classList.length === 0 && matchMedia('(prefers-color-scheme: dark)').matches);
}
function Point() {
const r = snowing ? 4 : 8;
const r = 8;
// progress below 0 is neglected, negative initial
// progress serves to introduce random delays -
@ -44,49 +40,38 @@ function Point() {
this.init = function() {
this.progress = initialProgress;
this.x = Math.random() * canvas.width;
this.y = snowing ? initialProgress * canvas.height / 3 : Math.random() * canvas.height;
this.r = snowing ? Math.random() * r : 0;
this.y = Math.random() * canvas.height;
this.r = 0;
this.rng = Math.random();
}
this.draw = function() {
if(this.progress >= 0) {
ctx.fillStyle = isDarkMode() ? FILL_STYLES.dark : FILL_STYLES.light;
ctx.beginPath();
// radius calculation: maps progress from [0, 1] to [0, pi],
// then takes sine of that to get an increase, then decrease
// in radius. absolute value to prevent floating point errors
// accidentally causing negative sine values which cause ctx.arc
// to throw errors
if(!snowing) ctx.arc(this.x, this.y, Math.abs(Math.sin(Math.PI*this.progress)*r), 0, 2*Math.PI);
else ctx.arc(
this.x + 20 * Math.sin(this.progress * 3 * Math.PI) + Math.cos(this.progress * (5*this.rng) * Math.PI),
this.y, this.r, 0, 2*Math.PI);
ctx.arc(this.x, this.y, Math.abs(Math.sin(Math.PI*this.progress)*r), 0, 2*Math.PI);
ctx.fill();
}
};
this.render = function() {
if(snowing) {
this.y += (Math.pow(this.rng, 0.25)) * 0.5 * (1 + Math.sin(this.progress % Math.PI));
this.x = (this.x + 0.3) % canvas.width; // wind
this.progress += 0.005;
this.draw();
if(this.y >= canvas.height + r) this.init();
}
else {
// stars come faster than they go
// so user can look at them longer
// i guess? idk this just looked pretty
if(this.progress > 0.5) this.progress += 0.005;
else this.progress += 0.05;
this.draw();
if(this.progress >= 1) this.init();
}
// stars come faster than they go
// so user can look at them longer
// i guess? idk this just looked pretty
if(this.progress > 0.5) this.progress += 0.005;
else this.progress += 0.05;
this.draw();
if(this.progress >= 1) this.init();
}
}
const dots = [];
const n = snowing ? 50 : 20;
const n = 20;
for(let i = 0; i < n; i++) {
dots[i] = new Point();
@ -124,62 +109,49 @@ document.querySelector('.age').textContent = `${tens[Math.floor(age / 10)]} ${on
// easter egg
const sequences = [
'CLAPOFF'.split('').map(s => 'Key' + s),
'CLAPON'.split('').map(s => 'Key' + s)
];
'DARK',
'LITE',
'PURP',
'MINT',
'YELL',
'BLUE',
'SAVE'
].map(seq => {
return {
word: seq,
combo: seq.split('').map(c => 'Key' + c)
}
});
let eei = 0;
const lastFourKeys = [];
addEventListener('keypress', e => {
const clapper = document.querySelector('.clapper');
const sequence = sequences[darkMode];
if(e.code == sequence[eei]) {
eei++;
}
else {
eei = 0;
}
if(eei == sequence.length) {
eei = 0;
clapper.classList.toggle('clapping');
setTimeout(() => {
if(!snowing) {
if(darkMode) {
document.body.classList.remove('dark-mode');
ctx.fillStyle = FILL_STYLES.light;
darkMode = 0;
}
else {
document.body.classList.add('dark-mode');
ctx.fillStyle = FILL_STYLES.dark;
darkMode = 1;
}
eei = 0;
} else {
if(darkMode) {
ctx.fillStyle = FILL_STYLES.snowing;
document.body.style.background = BACKGROUNDS.evening;
darkMode = 0;
}
else {
ctx.fillStyle = FILL_STYLES.dark;
document.body.style.background = BACKGROUNDS.night;
darkMode = 1;
lastFourKeys.push(e.code);
while(lastFourKeys.length > 4) lastFourKeys.shift();
sequences.forEach(({word, combo}) => {
if(combo.every((v, i) => v === lastFourKeys[i])) {
if(word === 'SAVE') {
if(confirm('Would you like to save the current color mode to local browser storage?')) {
localStorage.setItem('color-mode', document.body.getAttribute('class'));
}
return;
}
setTimeout(() => clapper.classList.toggle('clapping'), 500);
}, 500);
}
const clapper = document.querySelector('.clapper');
clapper.classList.toggle('clapping');
setTimeout(() => {
document.body.setAttribute('class', `${word.toLowerCase()}-mode`);
setTimeout(() => clapper.classList.toggle('clapping'), 500);
}, 500);
}
});
});
function among() {
alert('haha among us');
}
document.querySelector('.among').addEventListener('keypress', e => {
if(e.code === 'Space') {
e.preventDefault();
among();
}
document.querySelector('.among').addEventListener('click', e => {
e.preventDefault();
among();
});