CINXE.COM

leanrada.com

<!DOCTYPE html> <html lang="en"> <head> <title>leanrada.com</title> <link rel="preload" href="/fonts/space/SpaceMono-Italic.ttf" as="font" type="font/ttf" crossorigin=""> <link rel="preload" href="/fonts/iosevka/iosevka-custom-italic.woff2" as="font" type="font/woff2" crossorigin=""> <link rel="canonical" href="/"> <link rel="me" href="https://github.com/Kalabasa"> <script async="" src="/lib/vendor/perlin-noise-3d.min.js"></script> <link rel="preload" href="/fonts/space/SpaceMono-Italic.ttf" as="font" type="font/ttf" crossorigin=""> <link rel="icon" type="image/png" href="/favicon.png"> <link rel="webmention" href="https://webmention.io/leanrada.com/webmention"> <link rel="alternate" type="application/rss+xml" title="leanrada.com notes" href="/rss.xml"> <meta name="viewport" content="width=device-width, initial-scale=1"> <meta name="color-scheme" content="only dark"> <meta charset="utf-8"> <script async="" src="https://leanrada.com/analytics/analytics.js"></script> <style> .intro-line:nth-of-type(1) { animation-delay: 0.23862943611198906s; } .intro-line:nth-of-type(2) { animation-delay: 0.37725887222397814s; } .intro-line:nth-of-type(3) { animation-delay: 0.45835189384561104s; } .intro-line:nth-of-type(4) { animation-delay: 0.5158883083359672s; } .intro-line:nth-of-type(5) { animation-delay: 0.5605170185988092s; } .intro-line:nth-of-type(6) { animation-delay: 0.5969813299576001s; } .intro-line:nth-of-type(7) { animation-delay: 0.6278114659230517s; } </style> <style> .gh-contribs { display: inline-flex; gap: 18px; } .gh-contribs-icon { width: 48px; height: 48px; image-rendering: pixelated; filter: invert(1); } .gh-contribs-grid { display: grid; grid-auto-flow: column; grid-template-rows: repeat(7, auto); gap: 6px; } .gh-contribs-cell { position: relative; width: 12px; height: 12px; border-radius: 2px; box-sizing: border-box; background: #222c2c; color: transparent; overflow: hidden; user-select: none; } .gh-contribs-cell::after { content: ""; position: absolute; inset: 0; background: #54f8c1; } .gh-contribs-cell[data-level="0"]::after { opacity: 0; } .gh-contribs-cell[data-level="1"]::after { opacity: 0.3; } .gh-contribs-cell[data-level="2"]::after { opacity: 0.6; } .gh-contribs-cell[data-level="3"]::after { opacity: 1; } </style> <style> .hit-digit { margin: 0 1.5px; padding: 0 6px; border: solid 1px var(--text2-clr); border-radius: 6px; font-family: var(--display-font); font-weight: bold; } </style> <style> .right-now-emoji { font-size: 60px; min-height: 60px; } .right-now-status, .right-now-time, .right-now-disclaim { margin-top: 12px; min-height: 18px; } .right-now-disclaim { font-size: 6px; opacity: 0.1; } </style> <style> .card-component { border: solid 1px var(--card-clr); border-radius: 12px; padding: 18px 24px; box-sizing: border-box; background: var(--bg-clr); } @media (max-width: 462px) { .card-component { padding: 18px; } } </style> <style> .gallery-grid-item-block { position: relative; width: 100%; /* aspect ratio trick*/ padding-bottom: 100%; } .gallery-grid-item { position: absolute; display: block; width: 100%; height: 100%; border-radius: 18px; text-decoration: none; color: #fff; overflow: hidden; } .gallery-grid-media { position: absolute; /* yep overkill but it doesnt work without em */ left: 0; right: 0; top: 0; bottom: 0; width: 100%; height: 100%; display: block; object-fit: cover; } .gallery-grid-item.selected .gallery-grid-media, .gallery-grid-item:focus-visible .gallery-grid-media, .gallery-grid-item:hover .gallery-grid-media { filter: invert(1) contrast(0.6); } .gallery-grid-label { display: flex; justify-content: center; align-items: center; position: absolute; left: 0; right: 0; top: 0; bottom: 0; padding: 24px 18px 0; font-size: 15px; font-style: italic; font-weight: bold; line-height: 1.6; text-align: center; visibility: hidden; z-index: 0; } .gallery-grid-label::before { content: ""; position: absolute; left: 0; right: 0; top: 0; bottom: 0; background: var(--clr0-dark); opacity: 0.7; z-index: -1; } .gallery-grid-item.selected .gallery-grid-label, .gallery-grid-item:focus-visible .gallery-grid-label, .gallery-grid-item:hover .gallery-grid-label { visibility: visible; } .gallery-grid-item.selected .gallery-grid-label::after { content: "↗"; position: absolute; right: 18px; top: 18px; width: 18px; height: 18px; line-height: 16px; font-family: var(--display-font); font-size: 24px; font-style: normal; font-weight: bold; background: #fff; color: var(--clr0-dark); } </style> <style> .gallery-grid { display: grid; justify-content: center; margin: auto; max-width: 900px; grid-template-columns: repeat(3, 1fr); gap: 30px; } @media (max-width: 900px) { .gallery-grid { gap: 12px; } } @media (max-width: 600px) { .gallery-grid { grid-template-columns: repeat(2, 1fr); } } </style> <style> .blog-list { margin: auto; max-width: 800px; } .blog-list-heading { margin: 0; padding: 36px 12px 6px; border-bottom: solid 1px var(--card-clr); text-align: end; font-size: 16px; font-weight: normal; font-style: italic; font-family: var(--display-font); color: var(--text2-clr); } .blog-list-heading:first-child { padding-top: 0; } .blog-list-animated .blog-list-heading { animation: blog-list-heading-reveal 0.4s var(--ease) backwards; animation-delay: var(--blog-list-item-delay); } @keyframes blog-list-heading-reveal { 0% { opacity: 0; } 100% { opacity: 1; } } </style> <style> .blog-list-item { display: flex; justify-content: space-between; align-items: flex-start; gap: 6px; padding: 12px; border-bottom: solid 1px var(--card-clr); font-weight: bold; color: var(--text-clr); text-decoration: none; } .blog-list-animated .blog-list-item { animation: blog-list-item-reveal 0.4s var(--ease) backwards; animation-delay: var(--blog-list-item-delay); } @keyframes blog-list-item-reveal { 0% { clip-path: inset(0 0 100% 0); transform: translateY(100%); background: var(--card-clr); } 80% { clip-path: inset(0 0 0 0); transform: translateY(0); background: var(--card-clr); } 100% { background: transparent; } } .blog-list-item-title { font-family: var(--reading-font); font-size: 16px; text-align: start; } .blog-list-animated .blog-list-item-title .tag-component { animation: blog-list-item-tag-reveal 0.2s ease-out backwards; animation-delay: calc(0.4s + var(--blog-list-item-delay)); } .blog-list-animated .blog-list-item-title .tag-component:last-child { animation-delay: calc(0.45s + var(--blog-list-item-delay)); } @keyframes blog-list-item-tag-reveal { 0% { opacity: 0; filter: brightness(4) contrast(0.8); } 60% { opacity: 1; filter: brightness(4) contrast(0.8); } 100% { opacity: 1; filter: brightness(1); } } .blog-list-item-date { flex: 0 0 auto; font-weight: bold; color: var(--text2-clr); text-align: end; } @media (hover: hover) { .blog-list-item:hover { background: var(--card-clr); } .blog-list-item:hover .blog-list-item-title { color: var(--clr0-light); text-decoration: underline; } .blog-list-item:hover .blog-list-item-date { color: inherit; } } </style> <style> .tag-component { display: inline-block; padding: 0 3px; height: 16px; line-height: 16px; font-family: var(--default-font); font-size: 12px; font-weight: bold; background: #ccc; color: #444; border-radius: 3px; } </style> <style> .card-grid-item { display: block; text-decoration: none; text-align: start; color: var(--text-clr); overflow: hidden; transition: 0.2s var(--ease); transition-property: margin, padding; } .card-grid-animated .card-grid-item { animation: card-grid-item-reveal 0.8s var(--ease) backwards; animation-delay: var(--card-grid-item-delay); } @keyframes card-grid-item-reveal { 0% { clip-path: inset(20% 100% 60% 0 round 18px); filter: brightness(0); } 50% { clip-path: inset(20% 0 60% 0 round 18px); filter: brightness(1); } 100% { clip-path: inset(0 0 0 0 round 18px); } } .card-grid-image-container { position: relative; width: 100%; height: 270px; border-radius: 18px; overflow: hidden; z-index: 0; } .card-grid-image-container::after { content: ""; position: absolute; left: 0; right: 0; top: 0; bottom: 0; background: var(--clr0); mix-blend-mode: darken; transition: background 1s; } .card-grid-image { width: 101%; height: 101%; object-fit: cover; object-position: top; filter: contrast(0.8); transform: scale(1); transform-origin: 9px 9px; transition: filter 1s, transform 1s; } .card-grid-thumbalign-center .card-grid-image { object-position: center; transform-origin: center; } .card-grid-thumbalign-top .card-grid-image { object-position: top; transform-origin: top; } .card-grid-title-wrap { position: relative; max-width: 70%; height: 0; } .card-grid-title-bar { position: absolute; bottom: 0; border-top-right-radius: 18px; background: var(--bg-clr); } .card-grid-title-content { position: relative; padding: 12px 24px 0 0; transition: padding 0.2s var(--ease); } .card-grid-title-content::before { content: ""; position: absolute; left: 0; bottom: 100%; width: 18px; height: 36px; border-bottom-left-radius: 18px; box-shadow: 0 18px 0 var(--bg-clr); } .card-grid-title-content::after { content: ""; position: absolute; left: 100%; bottom: 0; width: 36px; height: 18px; border-bottom-left-radius: 18px; box-shadow: -18px 0 0 var(--bg-clr); } .card-grid-title { font-family: var(--display-font); font-size: 18px; font-style: italic; font-weight: bold; } .card-grid-posttitle { margin-top: 12px; font-size: 15px; font-style: italic; font-weight: bold; opacity: 0.8; } .card-grid-posttitle:empty { display: none; } .card-grid-postitle-small { display: none; } .card-grid-actions { display: none; gap: 5mm; max-height: 48px; padding-top: 6px; overflow: hidden; } .card-grid-action { display: inline-block; font-weight: bold; text-decoration: underline; color: var(--text2-clr); cursor: pointer; position: relative; } .card-grid-info { margin: -2.5mm; padding: 2.5mm; } .card-grid-story::after { content: "→"; display: inline-block; } .card-grid-content { margin: 18px 0; font-size: 15px; line-height: 2; transition: margin 0.2s var(--ease); } .card-grid-animated .card-grid-title { animation: card-grid-text-reveal 0.4s cubic-bezier(0, 0.8, 0.6, 1) backwards; animation-delay: calc(var(--card-grid-item-delay) + 0.8s); } .card-grid-animated .card-grid-posttitle { animation: card-grid-text-reveal 0.4s cubic-bezier(0, 0.8, 0.6, 1) backwards; animation-delay: calc(var(--card-grid-item-delay) + 0.9s); } @keyframes card-grid-text-reveal { from { transform: translateY(-100%); clip-path: inset(100% 0 0 0); } to { clip-path: inset(0 0 0 0); } } .card-grid-animated .card-grid-content { animation: card-grid-content 0.6s 1.5s backwards; } @keyframes card-grid-content { from { opacity: 0; } } @media (hover: hover) { .card-grid-item:hover { margin: -12px -24px 0; padding-bottom: 12px; } .card-grid-item:hover .card-grid-image-container::after { background: var(--text-clr); transition: background 2s; } .card-grid-item:hover .card-grid-image { filter: contrast(1); transform: scale(1.1); transition: filter 1s, transform 2s; } .card-grid-item:hover .card-grid-title-content { padding: 24px; } .card-grid-item:hover .card-grid-content { margin-left: 24px; margin-right: 24px; } } @media (max-width: 600px) { .card-grid-content { margin: 12px 0; } .card-grid-image-container { height: 200px; } .card-grid-posttitle { display: none; } .card-grid-postitle-small { display: block; } .card-grid-actions { display: flex; } .card-grid-item-expanded .card-grid-actions { max-height: 0; transition: max-height 0.2s cubic-bezier(0.2, 0, 0.8, 1); } .card-grid-description { max-height: 0; overflow: hidden; } .card-grid-item-expanded .card-grid-description { max-height: 100vh; transition: max-height 1s cubic-bezier(0.2, 0, 1, 1); } } </style> <style> .card-grid { display: grid; justify-content: center; grid-template-columns: repeat(auto-fit, calc(min(546px, 100%))); gap: 60px; } .card-grid-tall { gap: 90px 60px; } @media (max-width: 900px) { .card-grid { gap: 30px; } } @media (prefers-reduced-motion) { .card-grid * { animation: none !important; } } </style> <style> magnetic-field-client { cursor: pointer; -webkit-tap-highlight-color: rgba(0,0,0,0); } magnetic-field-client > canvas { width: 100%; height: 100%; } </style> <style> .great-time-counter { display: none; position: fixed; bottom: 0; right: 0; font-size: 15px; background-color: var(--bg-clr); pointer-events: none; transform-origin: bottom right; animation: great-time-counter-appear 0.6s both; z-index: 200; } .great-time-counter.flash { background-color: #fff; } @keyframes great-time-counter-appear { from { opacity: 0; transform: scale(4); } } </style> <style> .great-time-trigger { text-decoration: underline; cursor: pointer; user-select: none; } </style> <style> .text-link { color: var(--clr0-light, #fff); } .text-link:visited { color: var(--clr0, #fff); } </style> <style> .home-circle-icon { position: absolute; left: calc(50% - 2.5vh); top: calc(50% - 2.5vh); width: 5vh; height: 5vh; object-fit: cover; image-rendering: pixelated; filter: invert(1); } </style> <style> .curved { display: inline-block; width: 0; } </style> <style> .home-circle-text { position: relative; margin: 2vh; width: 100%; height: 100%; font-family: var(--display-font); font-size: 3vh; font-style: italic; color: #fff; animation: home-circle-text-animation 7s cubic-bezier(0.785, 0.135, 0.15, 0.86) infinite; } @keyframes home-circle-text-animation { to { transform: rotate(-1turn); } } </style> <style> .main-header { position: sticky; top: 0; width: 100%; height: 60px; padding: 0 calc(25% - 150px); z-index: 100; box-sizing: border-box; pointer-events: none; } .main-header.float { position: fixed; } .main-header-bar { position: relative; display: flex; justify-content: space-evenly; align-items: flex-end; height: 100%; } .main-header-item { display: inline-flex; justify-content: center; align-items: center; width: 6ch; height: 36px; padding: 0 18px; padding: 0 calc(min(18px, -18px + 8vw)); border-radius: 18px; font-family: var(--display-font); font-size: 12px; font-weight: bold; letter-spacing: 1px; text-decoration: none; text-transform: uppercase; color: #fff; backdrop-filter: blur(8px); background-color: #2222; background-image: linear-gradient(60deg, #9994 60%, transparent 60%); background-repeat: no-repeat; background-size: 220% 100%; background-position: 117%; pointer-events: all; transition: background-color 0.2s var(--ease), background-position 0.2s var(--ease); } .main-header-item:hover { background-image: linear-gradient( 60deg, #9994 50%, #fff 50%, #fff 60%, transparent 60% ); background-position: 0%; } .main-header-item.select { background: #9994; } .main-header-icon { width: 48px; height: 48px; border-radius: 25%; image-rendering: pixelated; object-fit: cover; filter: invert(1); backdrop-filter: blur(8px) invert(1); pointer-events: all; } .main-header-icon-yay { border-radius: 50%; object-position: 0 0; } .main-header-icon-yay:hover { object-position: 100% 0; } @media (max-width: 600px) { .main-header { height: 90px; padding-top: 6px; } .main-header-bar { display: grid; grid-template-columns: repeat(3, 1fr); justify-items: center; row-gap: 6px; } .main-header-icon { display: none; } } .main-header-indicator { position: absolute; width: 6px; height: 6px; left: 50%; top: calc(100% + 12px); transform: translate(-50%, -50%); /* border acts as extended touch area */ border: solid 6mm transparent; border-left-width: 45vw; border-right-width: 45vw; border-radius: 50%; background: #fff; background-clip: padding-box; opacity: 0; transition: opacity 50ms; } .main-header.hidden .main-header-indicator { opacity: 0.4; pointer-events: all; } </style> <style> @font-face { font-family: "Space Mono"; font-display: swap; font-weight: normal; font-style: normal; src: url("/fonts/space/SpaceMono-Regular.ttf") format("truetype"); } @font-face { font-family: "Space Mono"; font-display: swap; font-weight: normal; font-style: italic; src: url("/fonts/space/SpaceMono-Italic.ttf") format("truetype"); } @font-face { font-family: "Iosevka"; font-display: swap; font-weight: normal; font-style: normal; src: url("/fonts/iosevka/iosevka-custom-regular.woff2") format("woff2"); } @font-face { font-family: "Iosevka"; font-display: swap; font-weight: normal; font-style: italic; src: url("/fonts/iosevka/iosevka-custom-italic.woff2") format("woff2"); } @font-face { font-family: "Miriam Libre"; font-display: swap; font-weight: normal; font-style: normal; src: url("/fonts/miriam/MiriamLibre-Regular.ttf") format("truetype"); } :root { --default-font: "Iosevka", "Space Mono", monaco, Consolas, "Lucida Console", monospace; --display-font: "Space Mono", "Iosevka", monaco, Consolas, "Lucida Console", monospace; --reading-font: "Miriam Libre", Futura, "Trebuchet MS", Arial, sans-serif; --bg-clr: #111616; --card-clr: #222c2c; --clr0-light: #54f8c1; --clr0: #0ad591; --clr0-dark: #05b97d; --clr1: #df2063; --text-clr: #fff; --text2-clr: #999; --ease: cubic-bezier(0.8, 0, 1, 1); } body { margin: 0; min-height: 100vh; font-family: var(--default-font, monospace); font-size: 15px; font-display: swap; font-variant-ligatures: none; background-color: var(--bg-clr, #111); color: var(--text-clr, #fff); overflow-x: hidden; } .page-wrapper { display: flex; flex-direction: column; min-height: 100vh; } .page-wrapper-content { flex: 1 1 auto; } </style> <style> nebula-client { position: relative; } nebula-client > canvas { width: 100%; height: 100%; } .nebula-noise { position: absolute; inset: 0; background: url("/noise.png"); /* todo: encapsulate noise.png */ opacity: 0.1; animation: nebula-noise-x 0.16s steps(2, jump-start) infinite, nebula-noise-y 0.48s steps(3, jump-start) infinite; } @supports (mix-blend-mode: overlay) { .nebula-noise { mix-blend-mode: overlay; opacity: 0.2; } } @keyframes nebula-noise-x { to { background-position-x: 100px; } } @keyframes nebula-noise-y { to { background-position-y: 100px; } } </style> <style> .main-footer-web-link { text-decoration: none; } .main-footer-web-link-icon { width: 32px; height: 32px; image-rendering: pixelated; } .main-footer-web-link-icon:hover { filter: invert(1); } </style> <style> .main-footer { position: relative; display: flex; justify-content: space-between; gap: 60px; padding: 42px calc(max(36px, 50vw - 600px)); font-family: var(--display-font); font-size: 15px; font-weight: bold; background: var(--clr0); color: #000; overflow: hidden; content-visibility: auto; contain-intrinsic-height: 200px; } .main-footer-links-heading { margin: 18px 0; font-size: inherit; } .main-footer-line { margin: 12px 0; } .main-footer-link { color: #000; } .main-footer-link:hover { color: #000; } .main-footer-webring-icon { width: 16px; height: 16px; image-rendering: pixelated; } .main-footer-signature { width: 64px; height: 64px; image-rendering: pixelated; vertical-align: top; } .main-footer-content-part { z-index: 1; } .main-footer-nebula { position: absolute; left: -30%; top: -30%; width: 160%; height: 160%; z-index: 0; } .main-footer-top-btn { display: block; z-index: 2; position: absolute; right: 0; top: 0; width: 72px; height: 36px; padding-right: 9px; box-sizing: border-box; display: flex; justify-content: center; align-items: center; border: none; border-bottom-left-radius: 18px; background: var(--bg-clr); text-decoration: none; color: var(--clr0-light); font: inherit; font-size: 18px; font-weight: bold; cursor: pointer; } .main-footer-top-btn:hover { color: var(--clr1); } .main-footer-top-btn::before { content: ""; position: absolute; right: 0; top: 0; width: 36px; height: 18px; border-top-right-radius: 18px; transform: translateX(-72px); box-shadow: 18px 0 0 var(--bg-clr); } @media (max-width: 700px) { .main-footer { flex-direction: column-reverse; } } </style> <style> .header { display: flex; justify-content: center; align-items: center; height: clamp(95vh, 600px, 100vh); } @media (prefers-reduced-motion) { .header * { animation: none !important; } .nebula { visibility: hidden; } } .header-content { white-space: nowrap; height: 40vh; min-width: 80vh; overflow: hidden; } .header-circle { position: relative; display: block; float: left; width: 40vh; height: 40vh; box-sizing: content-box; margin: 0vh 2vh; border-radius: 50%; background: #bf1852; shape-outside: circle(calc(50% + 1vh)); text-align: initial; overflow: hidden; animation: header-circle-reveal 0.5s cubic-bezier(1, 0, 0.9, 1), fade-background-in 0.2s; } @keyframes header-circle-reveal { from { clip-path: circle(2.5vh); } to { clip-path: circle(50%); } } @keyframes fade-background-in { from { background: transparent; } } .nebula { position: absolute; left: 0; top: 0; width: 100%; height: 100%; } .header-circle-text-big { width: 36vh; height: 36vh; } .header-circle-text-small { display: none; } .header-icon { object-position: 0 0; } .header-circle:hover .curved { color: transparent; clip-path: inset(0.8ex -1ch 0); animation: header-circle-text-shadow-animation 0.6s cubic-bezier(0, 0, 0.2, 1) forwards; } @keyframes header-circle-text-shadow-animation { 0% { text-shadow: 0 8vh 0 #fff, 0 4vh 0 #fff, 0 0 0 #fff; } 100% { text-shadow: 0 0 0 #fff, 0 -4vh 0 #fff, 0 -8vh 0 #fff; } } .header-circle:hover .header-icon { object-position: 20% 0; } .intro-text { line-height: 1.8; font-size: 2.2vh; font-style: italic; animation: intro-text 0.8s 0.4s cubic-bezier(0, 0.7, 0.4, 1) backwards; } @keyframes intro-text { from { line-height: 8; } } .intro-line { display: inline-block; animation: intro-text-drift 0.9s cubic-bezier(0, 0.8, 0.2, 1) both; } .intro-line a, .intro-line a:visited { color: inherit; } @keyframes intro-text-drift { from { transform: translate(-1em, 4em); text-shadow: 0 3em 0 var(--clr0), 0 6em 0 var(--clr1); } to { transform: translate(0, 0); text-shadow: 0 0 0 var(--clr0), 0 0 0 var(--clr1); } } .intro-spacer { display: block; height: 0px; animation: intro-spacer 0.4s 0.4s cubic-bezier(0, 0.8, 0.1, 1) backwards; } @keyframes intro-spacer { from { height: 100%; } } .ipa { font-size: 85%; font-style: normal; text-decoration: none; color: inherit; } .ipa-text { text-decoration: underline; } .ipa-icon { display: inline-block; transform: scale(0.6); filter: brightness(8); } @media (max-aspect-ratio: 4/5) { .header { height: auto; min-height: 95vh; margin: 30px 0 60px; } .header-content { height: auto; min-width: unset; text-align: center; } .header-circle { display: inline-block; float: none; width: 30vh; height: 30vh; } .header-circle-text-big { display: none; } .header-circle-text-small { display: block; width: 26vh; height: 26vh; } .intro-text { display: block; margin-top: 4vh; font-size: 16px; animation: intro-text-mobile 1s cubic-bezier(0.8, 0.2, 0.2, 0.8); } .intro-spacer { animation: none; } } @keyframes intro-text-mobile { from { clip-path: inset(0 0 100% 0); } to { clip-path: inset(0 0 0 0); } } .section { max-width: 1200px; margin: 0 auto max(10vmin, 150px); padding: 0 60px; font-size: 15px; text-align: center; } .section:last-child { margin-bottom: 150px; } .section > p { margin: 30px 0; line-height: 2; } .section-title { margin: 0 0 36px; font-family: var(--display-font); font-size: 21px; font-style: italic; font-weight: bold; } @media (max-width: 800px) { .section { padding: 0 18px; } } .start-from-top .section { animation: section-appear 0.2s 1s backwards; } @keyframes section-appear { from { opacity: 0; } } .about-section { position: relative; display: flex; justify-content: center; flex-wrap: wrap; gap: 60px; z-index: 0; } .about-subsection { flex: 1 1 50%; max-width: 520px; text-align: start; } .magnetic-field { position: absolute; left: 50%; top: 50%; width: 100vw; height: 90%; transform: translate(-50%, -50%); z-index: -1; } .cursor-section { display: none; } @media (any-hover: hover) { .cursor-section { display: revert; } } .projects-section { max-width: unset; overflow: hidden; } .projects-section .section-title { margin-bottom: 60px; } .more-projects { display: inline-block; margin-top: 30px; font-weight: bold; } .more-projects::after { content: "→"; display: inline-block; } .more-notes { display: inline-block; margin-top: 30px; font-weight: bold; } .more-notes::after { content: "→"; display: inline-block; } .misc-section { position: relative; display: flex; justify-content: center; flex-wrap: wrap; gap: 36px; } .misc-subsection { display: flex; flex-direction: column; align-items: center; justify-content: center; flex: 1 1 50%; min-height: 195px; max-width: 390px; text-align: center; } @media (max-width: 462px) { .misc-section { gap: 18px; } .misc-subsection { min-height: 130px; } } .more-misc { display: inline-block; font-weight: bold; text-align: center; } .more-misc::after { content: "→"; display: inline-block; } .hit-counter-subsection { font-size: 36px; } </style> <script async="" src="/scripts/cursor.js"></script> </head> <body> <div class="page-wrapper"> <div class="page-wrapper-content"> <header class="main-header float prehide"> <div class="main-header-bar"> <a href="/" class="main-header-item select">Home</a> <a href="/notes/" class="main-header-item ">Notes</a> <a href="/about/" class="main-header-item ">About</a> <img class="main-header-icon main-header-icon-yay" src="/icons/yay_sheet.png" alt=""> <a href="/wares/" class="main-header-item ">Wares</a> <a href="/art/" class="main-header-item ">Art</a> <a href="/music/" class="main-header-item ">Music</a> <div class="main-header-indicator"></div> </div> </header> <section class="header"> <div class="header-content"> <div class="header-circle"> <nebula-client class="nebula" palette="#e79907 #bf1852 #4d4aff #0ad591" mouse=""> <canvas style="filter: contrast(1.5)"></canvas> <div class="nebula-noise"></div> </nebula-client> <div class="home-circle-text header-circle-text-big"> <span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(-108.75deg); transform-origin: 0.5ch 18vh">W</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(-101.25deg); transform-origin: 0.5ch 18vh">e</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(-93.75deg); transform-origin: 0.5ch 18vh">l</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(-86.25deg); transform-origin: 0.5ch 18vh">c</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(-78.75deg); transform-origin: 0.5ch 18vh">o</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(-71.25deg); transform-origin: 0.5ch 18vh">m</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(-63.75deg); transform-origin: 0.5ch 18vh">e</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(-56.25deg); transform-origin: 0.5ch 18vh"> </span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(-48.75deg); transform-origin: 0.5ch 18vh">t</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(-41.25deg); transform-origin: 0.5ch 18vh">o</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(-33.75deg); transform-origin: 0.5ch 18vh"> </span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(-26.25deg); transform-origin: 0.5ch 18vh">m</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(-18.75deg); transform-origin: 0.5ch 18vh">y</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(-11.25deg); transform-origin: 0.5ch 18vh"> </span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(-3.75deg); transform-origin: 0.5ch 18vh">p</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(3.75deg); transform-origin: 0.5ch 18vh">e</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(11.25deg); transform-origin: 0.5ch 18vh">r</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(18.75deg); transform-origin: 0.5ch 18vh">s</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(26.25deg); transform-origin: 0.5ch 18vh">o</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(33.75deg); transform-origin: 0.5ch 18vh">n</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(41.25deg); transform-origin: 0.5ch 18vh">a</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(48.75deg); transform-origin: 0.5ch 18vh">l</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(56.25deg); transform-origin: 0.5ch 18vh"> </span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(63.75deg); transform-origin: 0.5ch 18vh">w</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(71.25deg); transform-origin: 0.5ch 18vh">e</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(78.75deg); transform-origin: 0.5ch 18vh">b</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(86.25deg); transform-origin: 0.5ch 18vh">s</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(93.75deg); transform-origin: 0.5ch 18vh">i</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(101.25deg); transform-origin: 0.5ch 18vh">t</span><span class="curved" style="transform: translateX(calc(18vh - 0.5ch)) rotate(108.75deg); transform-origin: 0.5ch 18vh">e</span> </div> <div class="home-circle-text header-circle-text-small"> <span class="curved" style="transform: translateX(calc(13vh - 0.5ch)) rotate(-81deg); transform-origin: 0.5ch 13vh">W</span><span class="curved" style="transform: translateX(calc(13vh - 0.5ch)) rotate(-72deg); transform-origin: 0.5ch 13vh">e</span><span class="curved" style="transform: translateX(calc(13vh - 0.5ch)) rotate(-63deg); transform-origin: 0.5ch 13vh">l</span><span class="curved" style="transform: translateX(calc(13vh - 0.5ch)) rotate(-54deg); transform-origin: 0.5ch 13vh">c</span><span class="curved" style="transform: translateX(calc(13vh - 0.5ch)) rotate(-45deg); transform-origin: 0.5ch 13vh">o</span><span class="curved" style="transform: translateX(calc(13vh - 0.5ch)) rotate(-36deg); transform-origin: 0.5ch 13vh">m</span><span class="curved" style="transform: translateX(calc(13vh - 0.5ch)) rotate(-27deg); transform-origin: 0.5ch 13vh">e</span><span class="curved" style="transform: translateX(calc(13vh - 0.5ch)) rotate(-18deg); transform-origin: 0.5ch 13vh"> </span><span class="curved" style="transform: translateX(calc(13vh - 0.5ch)) rotate(-9deg); transform-origin: 0.5ch 13vh">t</span><span class="curved" style="transform: translateX(calc(13vh - 0.5ch)) rotate(0deg); transform-origin: 0.5ch 13vh">o</span><span class="curved" style="transform: translateX(calc(13vh - 0.5ch)) rotate(9deg); transform-origin: 0.5ch 13vh"> </span><span class="curved" style="transform: translateX(calc(13vh - 0.5ch)) rotate(18deg); transform-origin: 0.5ch 13vh">m</span><span class="curved" style="transform: translateX(calc(13vh - 0.5ch)) rotate(27deg); transform-origin: 0.5ch 13vh">y</span><span class="curved" style="transform: translateX(calc(13vh - 0.5ch)) rotate(36deg); transform-origin: 0.5ch 13vh"> </span><span class="curved" style="transform: translateX(calc(13vh - 0.5ch)) rotate(45deg); transform-origin: 0.5ch 13vh">s</span><span class="curved" style="transform: translateX(calc(13vh - 0.5ch)) rotate(54deg); transform-origin: 0.5ch 13vh">i</span><span class="curved" style="transform: translateX(calc(13vh - 0.5ch)) rotate(63deg); transform-origin: 0.5ch 13vh">t</span><span class="curved" style="transform: translateX(calc(13vh - 0.5ch)) rotate(72deg); transform-origin: 0.5ch 13vh">e</span><span class="curved" style="transform: translateX(calc(13vh - 0.5ch)) rotate(81deg); transform-origin: 0.5ch 13vh">!</span> </div> <img class="home-circle-icon header-icon" src="/icons/yay_sheet.png" alt=""> </div> <span class="intro-text"> <div class="intro-spacer"></div> <span id="greeting" class="intro-line"><span id="home-greeting">hello!</span></span> <br> <span class="intro-line"> I’m Lean <a target="_blank" class="ipa" href="https://ipa-reader.xyz/?text=lian">/<span class="ipa-text">liˈan</span>/<span class="ipa-icon">🔊</span></a>, </span> <br> <span class="intro-line"> software engineer </span> <br> <span class="intro-line"> who likes making <a target="_self" class="text-link " href="/notes/">stuff</a>, </span> <br> <span class="intro-line"> <a target="_self" class="text-link " href="/wares/">code</a>, <a target="_self" class="text-link " href="/art/">art</a>, and <a target="_self" class="text-link " href="/music/">music</a>. </span> <br> <br> <span class="intro-line"> Nice to meet you. </span> <br> <span class="intro-line"> Please, <span class="great-time-trigger" onclick="haveAGreatTime(event)">have a great time.</span> </span> </span> </div> </section> <div id="great-time-counter" class="great-time-counter"> you now have <span id="great-time-num">0</span> great time(s) </div> <div class="section about-section"> <section class="about-subsection"> <h2 class="section-title">About this site</h2> <p>This site contains:</p> <ul> <li> A <a target="_self" class="text-link " href="/wares/">portfolio</a> of some of my projects </li> <li>Small section of my <a target="_self" class="text-link " href="/art/">art</a></li> <li> Programming <a target="_self" class="text-link " href="/notes/">blog</a> with interactive visualisations </li> <li>My <a target="_self" class="text-link " href="/music/">music</a></li> <li>Links to my other stuff</li> <li>idk, <a target="_self" class="text-link " href="/misc/">random things</a></li> <li>and more?</li> </ul> </section> <section class="about-subsection"> <h2 class="section-title">About me</h2> <p>My name is Lean Rada.</p> <p>I want to build good things.</p> <p> My interests are programming, interactive things, procedural animations, algorithms, art, music theory, games, and tech. </p> <p> Currently working at <a target="_blank" class="text-link " href="https://canva.com">Canva</a> doing frontend stuff. </p> <p>More <a target="_self" class="text-link " href="/about/">about me</a>.</p> </section> <magnetic-field-client class="magnetic-field"> <canvas> </canvas></magnetic-field-client> </div> <section class="section cursor-section"> <div class="cursor-provider" aria-hidden="true"> <h2 class="cursor-provider-title"><em>It’s dangerous to go alone!</em><br>Take these <strong>free cursors</strong>!</h2> <h2> <button class="cursor-provider-button" data-cursor="note"></button> <button class="cursor-provider-button" data-cursor="sword"></button> <button class="cursor-provider-button" data-cursor="boba"></button> </h2> </div> <style> .cursor-provider { display: inline-block; padding: 0 24px 12px; border: solid 1px var(--card-clr); border-radius: 12px; text-align: center; } .cursor-provider-title { font-size: 18px; font-weight: normal; line-height: 27px; } .cursor-provider-button { cursor: inherit; width: 60px; height: 60px; border: inset 4px var(--text-clr); background-color: var(--bg-clr); background-size: 24px 24px; background-position: center; background-repeat: no-repeat; image-rendering: pixelated; } .cursor-provider-button:hover { border-color: var(--clr0); background-position: center calc(50% - 4px); } </style> <script client="" async="" defer="" src="/scripts/index_html.js"></script> </section> <section class="section projects-section"> <h2 class="section-title">Some things I’ve made</h2> <div class="card-grid "> <a class="card-grid-item card-grid-thumbalign-center" href="/wares/wikawik/"> <div class="card-grid-image-container"> <img srcset="/gen/_wares_wikawik_media_wikawik_546.generated.jpg 546w" sizes=" 546px" spec="546" loading="lazy" width="100%" data-placeholder="" style=";aspect-ratio: 1;background: #48c8d8" class="card-grid-image" src="/wares/wikawik/media/wikawik.jpg" alt=""> </div> <div class="card-grid-title-wrap"> <div class="card-grid-title-bar"> <div class="card-grid-title-content"> <div class="card-grid-title">Wikawik</div> <div class="card-grid-posttitle">2020 · Interactive language map</div> </div> </div> </div> <div class="card-grid-content"> <div class="card-grid-posttitle card-grid-postitle-small"> 2020 · Interactive language map </div> <div class="card-grid-actions"> <span class="card-grid-action card-grid-info">Deets…</span> <span class="card-grid-action card-grid-story">Read post</span> </div> <div class="card-grid-description"> A webapp that shows the diversity of languages in the Philippines. Learn about the local languages. Listen to local songs and other media. Made in vanilla JS with D3.js. Passion project. <span class="card-grid-action card-grid-story" aria-label="Read post - Wikawik">Read post</span> </div> </div> </a> <a class="card-grid-item card-grid-thumbalign-center" href="/wares/hypertangram/"> <div class="card-grid-image-container"> <img srcset="/gen/_wares_hypertangram_media_ht-thumb_546.generated.png 546w" sizes=" 546px" spec="546" loading="lazy" width="100%" data-placeholder="" style=";aspect-ratio: 1;background: #0838a8" class="card-grid-image" src="/wares/hypertangram/media/ht-thumb.png" alt=""> </div> <div class="card-grid-title-wrap"> <div class="card-grid-title-bar"> <div class="card-grid-title-content"> <div class="card-grid-title">Hypertangram</div> <div class="card-grid-posttitle">2018 · Geometric puzzle game</div> </div> </div> </div> <div class="card-grid-content"> <div class="card-grid-posttitle card-grid-postitle-small"> 2018 · Geometric puzzle game </div> <div class="card-grid-actions"> <span class="card-grid-action card-grid-info">Deets…</span> <span class="card-grid-action card-grid-story">Read post</span> </div> <div class="card-grid-description"> A puzzle game about fitting shapes together. Just like tangram, but the pieces are resizable! Runs on Android. Supports multi-touch interactions for rotating and resizing! Hobby project. <span class="card-grid-action card-grid-story" aria-label="Read post - Hypertangram">Read post</span> </div> </div> </a> <a class="card-grid-item card-grid-thumbalign-center" href="/wares/dimensions/"> <div class="card-grid-image-container"> <img srcset="/gen/_wares_dimensions_media_dimensions_0_546.generated.jpg 546w" sizes=" 546px" spec="546" loading="lazy" width="100%" data-placeholder="" style=";aspect-ratio: 1;background: #a8c8a8" class="card-grid-image" src="/wares/dimensions/media/dimensions_0.jpg" alt=""> </div> <div class="card-grid-title-wrap"> <div class="card-grid-title-bar"> <div class="card-grid-title-content"> <div class="card-grid-title">Dimensions</div> <div class="card-grid-posttitle">2019 · Augmented reality generative art</div> </div> </div> </div> <div class="card-grid-content"> <div class="card-grid-posttitle card-grid-postitle-small"> 2019 · Augmented reality generative art </div> <div class="card-grid-actions"> <span class="card-grid-action card-grid-info">Deets…</span> <span class="card-grid-action card-grid-story">Read post</span> </div> <div class="card-grid-description"> Generative art with extra dimensions. Web-based, uses Vue.js for the generator and a three.js-based mobile webapp for the AR experience. Made for a workplace event. <span class="card-grid-action card-grid-story" aria-label="Read post - Dimensions">Read post</span> </div> </div> </a> </div> <a target="_self" class="text-link more-projects" href="/wares/"> More <abbr aria-label="software" title="software">s/w</abbr> projects</a> </section> <section class="section notes-section"> <h2 class="section-title">Latest notes</h2> <div class="blog-list undefined"> <!-- textlint-disable --> <a class="blog-list-item" href="/notes/css-sprite-sheets/"> <span class="blog-list-item-title"> CSS sprite sheet animations <span class="tag-component p-category" style="background: #9b78ba; color: #3c2c49">css</span> </span> <span class="blog-list-item-date"> 3 Nov 2024 </span> </a><a class="blog-list-item" href="/notes/how-to-center-in-css/"> <span class="blog-list-item-title"> Centering a div in a div in 2020 <span class="tag-component p-category" style="background: #9b78ba; color: #3c2c49">css</span> <span class="tag-component p-category" title="micropost" style="background: #cccccc; color: #444444">µpost</span> </span> <span class="blog-list-item-date"> 17 Oct 2024 </span> </a><a class="blog-list-item" href="/notes/motion-sickness-app/"> <span class="blog-list-item-title"> I made an app to fix my motion sickness <span class="tag-component p-category" style="background: #84d39e; color: #265936">android</span> </span> <span class="blog-list-item-date"> 2 Sept 2024 </span> </a><a class="blog-list-item" href="/notes/stop-using-ease-out/"> <span class="blog-list-item-title"> Stop using ease-out in your UIs! <span class="tag-component p-category" style="background: #9b78ba; color: #3c2c49">css</span> <span class="tag-component p-category" title="essay" style="background: #eeeeee; color: #4a4a4a">essay</span> </span> <span class="blog-list-item-date"> 15 Jul 2024 </span> </a> <!-- textlint-enable --> </div> <a target="_self" class="text-link more-notes" href="/notes/"> More notes</a> </section> <section class="section labs-section"> <h2 class="section-title">From the lab</h2> <p><em>warning: unpolished experiences!</em></p> <div class="gallery-grid"> <div class="gallery-grid-item-block"> <a target="_blank" class="gallery-grid-item" href="https://leanrada.com/guhit-kudlit/"> <video muted="" autoplay="" loop="" playsinline="" aria-label="Baybayin calligraphy generator" class="gallery-grid-media" alt="Baybayin calligraphy generator" loading="lazy" data-src="/labs/baybayin.mp4"> <source src="/labs/baybayin.mp4"> Video: Baybayin calligraphy generator | Source: /labs/baybayin.mp4 </video> <span class="gallery-grid-label">Baybayin calligraphy generator</span> </a> </div> <div class="gallery-grid-item-block"> <a target="_blank" class="gallery-grid-item" href="https://kalabasa.github.io/genuary_2023/09/"> <img srcset="/gen/_labs_wc-sim-9_280.generated.png 280w" sizes=" 280px" spec="280" loading="lazy" width="100%" data-placeholder="" style=";aspect-ratio: 1;background: #f8f8e8" class="gallery-grid-media" alt="Flowers in simulated watercolour [GPU heavy!]" src="/labs/wc-sim-9.png"> <span class="gallery-grid-label">Flowers in simulated watercolour [GPU heavy!]</span> </a> </div> <div class="gallery-grid-item-block"> <a target="_blank" class="gallery-grid-item" href="https://codepen.io/kalabasa/pen/OJLQEWY"> <video muted="" autoplay="" loop="" playsinline="" aria-label="3D hamburger menu animation" class="gallery-grid-media" alt="3D hamburger menu animation" loading="lazy" data-src="/labs/3d-hamburger.opt.mp4"> <source src="/labs/3d-hamburger.opt.mp4"> Video: 3D hamburger menu animation | Source: /labs/3d-hamburger.opt.mp4 </video> <span class="gallery-grid-label">3D hamburger menu animation</span> </a> </div> <div class="gallery-grid-item-block"> <a target="_blank" class="gallery-grid-item" href="https://codepen.io/kalabasa/pen/oVMOZK"> <img srcset="/gen/_labs_pure-css-ai-tic-tac-toe_280.generated.png 280w" sizes=" 280px" spec="280" loading="lazy" width="100%" data-placeholder="" style=";aspect-ratio: 1;background: #f8f8f8" class="gallery-grid-media" alt="Tic-tac-toe AI written in pure CSS. Can you beat a stylesheet?" src="/labs/pure-css-ai-tic-tac-toe.png"> <span class="gallery-grid-label">Tic-tac-toe AI written in pure CSS. Can you beat a stylesheet?</span> </a> </div> <div class="gallery-grid-item-block"> <a target="_blank" class="gallery-grid-item" href="https://kalabasa.github.io/genuary_2022/04/"> <img srcset="/gen/_labs_floridablanca_280.generated.png 280w" sizes=" 280px" spec="280" loading="lazy" width="100%" data-placeholder="" style=";aspect-ratio: 1;background: #f8f8e8" class="gallery-grid-media" alt="Floridablanca - flow art generator" src="/labs/floridablanca.png"> <span class="gallery-grid-label">Floridablanca - flow art generator</span> </a> </div> <div class="gallery-grid-item-block"> <a target="_blank" class="gallery-grid-item" href="https://kalabasa.github.io/genuary_2023/28/"> <img srcset="/gen/_labs_poetry-gen_280.generated.png 280w" sizes=" 280px" spec="280" loading="lazy" width="100%" data-placeholder="" style=";aspect-ratio: 1;background: #f8f8f8" class="gallery-grid-media" alt="Poetry generator" src="/labs/poetry-gen.png"> <span class="gallery-grid-label">Poetry generator</span> </a> </div> </div> </section> <section class="section misc-section"> <div class="card-component misc-subsection"> <right-now-client> <div class="right-now-emoji"></div> <div class="right-now-status"></div> <div class="right-now-time"></div> <div class="right-now-disclaim">- Lean Emulator</div> </right-now-client> </div> <div class="card-component misc-subsection hit-counter-subsection"> <h2 class="section-title">Hit counter</h2> <bump-tally-client> <span class="hit-digit">6</span><span class="hit-digit">4</span><span class="hit-digit">6</span><span class="hit-digit">3</span><span class="hit-digit">2</span> </bump-tally-client> </div> <div class="card-component misc-subsection"> <h2 class="section-title">GitHub activity</h2> <div class="gh-contribs"> <img class="gh-contribs-icon" alt="GitHub" src="/icons/github.png" loading="lazy"> <div class="gh-contribs-grid"> <div class="gh-contribs-cell" data-level="2">2</div> <div class="gh-contribs-cell" data-level="3">3</div> <div class="gh-contribs-cell" data-level="1">1</div> <div class="gh-contribs-cell" data-level="2">2</div> <div class="gh-contribs-cell" data-level="1">1</div> <div class="gh-contribs-cell" data-level="2">2</div> <div class="gh-contribs-cell" data-level="0">0</div> <div class="gh-contribs-cell" data-level="0">0</div> <div class="gh-contribs-cell" data-level="3">3</div> <div class="gh-contribs-cell" data-level="2">2</div> <div class="gh-contribs-cell" data-level="2">2</div> <div class="gh-contribs-cell" data-level="1">1</div> <div class="gh-contribs-cell" data-level="1">1</div> <div class="gh-contribs-cell" data-level="0">0</div> <div class="gh-contribs-cell" data-level="0">0</div> <div class="gh-contribs-cell" data-level="1">1</div> <div class="gh-contribs-cell" data-level="1">1</div> <div class="gh-contribs-cell" data-level="1">1</div> <div class="gh-contribs-cell" data-level="1">1</div> <div class="gh-contribs-cell" data-level="0">0</div> <div class="gh-contribs-cell" data-level="1">1</div> <div class="gh-contribs-cell" data-level="0">0</div> <div class="gh-contribs-cell" data-level="1">1</div> <div class="gh-contribs-cell" data-level="1">1</div> <div class="gh-contribs-cell" data-level="2">2</div> <div class="gh-contribs-cell" data-level="1">1</div> <div class="gh-contribs-cell" data-level="2">2</div> <div class="gh-contribs-cell" data-level="0">0</div> <div class="gh-contribs-cell" data-level="1">1</div> <div class="gh-contribs-cell" data-level="2">2</div> <div class="gh-contribs-cell" data-level="1">1</div> <div class="gh-contribs-cell" data-level="2">2</div> <div class="gh-contribs-cell" data-level="2">2</div> </div> </div> </div> <div class="card-component misc-subsection"> <a target="_self" class="text-link more-misc" href="/misc/"> More misc stuff</a> </div> </section> </div> <footer class="main-footer"> <div class="main-footer-content-part"> <p class="main-footer-line"> <a target="_self" class="main-footer-link" href="/">Home</a> · <a target="_self" class="main-footer-link" href="/notes/">Notes</a> · <a target="_self" class="main-footer-link" href="/about/">About</a> · <a target="_self" class="main-footer-link" href="/wares/">Software</a> · <a target="_self" class="main-footer-link" href="/art/">Art</a> · <a target="_self" class="main-footer-link" href="/music/">Music</a> </p> <p class="main-footer-line"> <img class="main-footer-signature" alt="" src="/icons/laptop_user.png" loading="lazy"> <span style="display: inline-block"> · <a target="_self" class="main-footer-link" href="/guestbook/">Sign the guestbook!</a> <br> · Drop a <a target="_blank" class="main-footer-link" href="mailto:mail%40leanrada.com"> mail@leanrada·com </a> <br> </span> </p> <p class="main-footer-line main-footer-best-viewed"> This site is best viewed with a cup of hot chocolate. </p> <p class="main-footer-line"> (C) 2015-2024 Lean Rada. <a target="_blank" class="main-footer-link" href="https://github.com/Kalabasa/kalabasa.github.io"> Source</a>. </p> </div> <div class="main-footer-content-part"> <h2 class="main-footer-links-heading">On the web</h2> <a target="_blank" class="main-footer-web-link" href="https://mastodon.social/@Kalabasa" rel="me"> <img class="main-footer-web-link-icon" alt="Mastodon" src="/icons/mastodon.png" loading="lazy"> </a> <a target="_blank" class="main-footer-web-link" href="https://codepen.io/kalabasa"> <img class="main-footer-web-link-icon" alt="CodePen" src="/icons/codepen.png" loading="lazy"> </a> <a target="_blank" class="main-footer-web-link" href="https://github.com/Kalabasa" rel="me"> <img class="main-footer-web-link-icon" alt="GitHub" src="/icons/github.png" loading="lazy"> </a> <h2 class="main-footer-links-heading">Webrings</h2> <p class="main-footer-line"> <img class="main-footer-webring-icon" alt="" src="/icons/planet.png" loading="lazy"> <a target="_blank" class="main-footer-link" href="http://geekring.net/">geekring.net</a> [<a target="_blank" class="main-footer-link" href="http://geekring.net/site/288/previous" aria-label="Previous site">←</a> <a target="_blank" class="main-footer-link" href="http://geekring.net/site/288/random" aria-label="Random site">⁙</a> <a target="_blank" class="main-footer-link" href="http://geekring.net/site/288/next" aria-label="Next site">→</a> <a target="_blank" class="main-footer-link" href="http://geekring.net/site/288/frameset" aria-label="Frameset browsing">▣</a>] </p> <p class="main-footer-line"> <img class="main-footer-webring-icon" alt="" src="/icons/planet.png" loading="lazy"> <a target="_blank" class="main-footer-link" href="https://cs.sjoy.lol/">CSS JOY</a> [<a target="_blank" class="main-footer-link" href="https://webri.ng/webring/cssjoy/previous?via=https%3A%2F%2Fleanrada.com" aria-label="Previous site">←</a> <a target="_blank" class="main-footer-link" href="https://webri.ng/webring/cssjoy/random?via=https%3A%2F%2Fleanrada.com" aria-label="Random site">⁙</a> <a target="_blank" class="main-footer-link" href="https://webri.ng/webring/cssjoy/next?via=https%3A%2F%2Fleanrada.com" aria-label="Next site">→</a>] </p> </div> <nebula-client class="main-footer-nebula" palette="#0ad591 #ff2b75 #ffb833 #0ad591 #0ad591 #4d4aff #0ad591" width="40" height="10"> <canvas style="filter: contrast(1.5)"></canvas> <div class="nebula-noise"></div> </nebula-client> <a class="main-footer-top-btn" href="#top" aria-label="Back to top">^</a> </footer> </div> <script type="module" async="" defer="" src="/scripts/bump-tally.js"></script> <script type="module" async="" defer="" src="/scripts/right-now.js"></script> <script defer=""> (() => { let hasTouch = false; document.addEventListener("touchstart", () => (hasTouch = true), { once: true, }); const items = document.querySelectorAll(".gallery-grid-item"); let selectedItem = null; // in touchscreens: first tap selects the item, second tap opens the link document.addEventListener("click", (event) => { if (selectedItem) { selectedItem.classList.remove("selected"); } if (!hasTouch) return; for (const item of items) { if (!item.contains(event.target)) continue; if (selectedItem === item) { return; // proceed with link } else { event.preventDefault(); item.classList.add("selected"); selectedItem = item; return; } } // loop exhausted, no clicked item selectedItem = null; }); })(); </script> <script defer="" src="/scripts/responsive-media.js"></script> <script defer="" src="/scripts/card-grid-item.js"></script> <script type="module" async="" defer="" src="/scripts/magnetic-field.js"></script> <script defer=""> (() => { const container = document.getElementById("great-time-counter"); const num = document.getElementById("great-time-num"); let greatTimes = 0; window.haveAGreatTime = function (event) { event.preventDefault(); event.stopPropagation(); greatTimes++; num.innerText = greatTimes; if (greatTimes === 1) { container.style.display = "block"; } else { container.classList.add("flash"); setTimeout(() => container.classList.remove("flash"), 34); } }; })(); </script> <script defer=""> const greeting = document.getElementById("greeting"); let text = greeting.textContent; // todo: more? text = pick("Hello!", "What’s up ↑", "Hi there →", "Hey there →"); greeting.textContent = text; function pick(...choices) { return choices[Math.floor(Math.random() * choices.length)]; } </script> <script type="module" async="" defer="" src="/scripts/nebula.js"></script> <script defer="" src="/scripts/main-footer-main-header.js"></script> <script defer=""> (() => { document.body.classList.toggle("start-from-top", window.scrollY < 100); const yayIcon = document.querySelector(".header-icon"); const sayLean = new Audio("/lean.mp3"); let sayTime = 0; const ipa = document.querySelector(".ipa"); ipa.addEventListener("click", (event) => { if (sayLean.readyState >= /*HAVE_ENOUGH_DATA*/ 4) { event.preventDefault(); sayLean.play(); } }); function animateYay() { const t = (Date.now() - sayTime) / (sayLean.duration * 1000); if (t >= 1) { yayIcon.style.objectPosition = null; } else { const frame = Math.floor(lerp(0, 3, t)); if (frame >= 0 && frame <= 2) { yayIcon.style.objectPosition = 40 + (frame % 3) * 20 + "% 0"; } else if (frame > 2) { yayIcon.style.objectPosition = "40% 0"; } else { yayIcon.style.objectPosition = null; } requestAnimationFrame(animateYay); } } sayLean.addEventListener("play", () => { // hardcoded offset sayTime = Date.now() + 300; animateYay(); }); function lerp(a, b, t) { return a + (b - a) * t; } })(); </script> </body> </html>

Pages: 1 2 3 4 5 6 7 8 9 10