diff --git a/assets/styles.css b/assets/styles.css index 7c3fca3..397b62f 100644 --- a/assets/styles.css +++ b/assets/styles.css @@ -1,552 +1,549 @@ @font-face { - font-family: "Jersey 15"; - src: url("/fonts/jersey15.ttf") format("truetype"); -} - -@font-face { - font-family: "Share Tech"; - src: url("/fonts/str.ttf") format("truetype"); + font-family: "Jersey 15"; + src: url("/fonts/jersey15.ttf") format("truetype"); } :root { - --theme-bg: #16191c; - --theme-bg-secondary: #252a2e; - --theme-bg-tertiary: #1b1f22; - --theme-fg: #a3aaaf; - --theme-fg-alt-rgb: 106, 111, 121; - --theme-fg-alt: rgb(var(--theme-fg-alt-rgb)); - --theme-accent-rgb: 163, 170, 175; /* 147, 184, 217; */ - --theme-accent: rgb(var(--theme-accent-rgb)); - --theme-accent-alt: #2c3237; /* #2a353e; */ - --theme-accent-third: rgb(140, 155, 189); /* #2a353e; */ - --theme-accent-title-rgb: var(--theme-accent-rgb); - --theme-accent-title: rgb(var(--theme-accent-title-rgb)); - --theme-border-rgb: 56, 60, 66; - --theme-border: rgb(var(--theme-border-rgb)); + --theme-bg: #16191c; + --theme-bg-secondary: #252a2e; + --theme-bg-tertiary: #1b1f22; + --theme-fg: #a3aaaf; + --theme-fg-alt-rgb: 106, 111, 121; + --theme-fg-alt: rgb(var(--theme-fg-alt-rgb)); + --theme-accent-rgb: 163, 170, 175; /* 147, 184, 217; */ + --theme-accent: rgb(var(--theme-accent-rgb)); + --theme-accent-alt: #2c3237; /* #2a353e; */ + --theme-accent-third: rgb(140, 155, 189); /* #2a353e; */ + --theme-accent-title-rgb: var(--theme-accent-rgb); + --theme-accent-title: rgb(var(--theme-accent-title-rgb)); + --theme-border-rgb: 56, 60, 66; + --theme-border: rgb(var(--theme-border-rgb)); } html { - line-height: 1.5; - font-family: "Jersey 15", sans-serif, "Share Tech", monospace; - font-size: 22px; - -webkit-font-smoothing: none; - font-smooth: never; + line-height: 1.5; + font-family: "Jersey 15", sans-serif, monospace; + font-size: 22px; + -webkit-font-smoothing: none; + font-smooth: never; + scroll-behavior: smooth; } body { - padding: 16px; - margin: auto; - background-color: var(--theme-bg); - color: var(--theme-fg); + padding: 16px; + margin: auto; + background-color: var(--theme-bg); + color: var(--theme-fg); } .section { - display: flex; - flex-direction: column; - padding: 2.4rem; - overflow-wrap: break-word; - clip-path: polygon( - 0px calc(100% - 8px), - 8px calc(100% - 8px), - 8px 100%, - calc(100% - 8px) 100%, - calc(100% - 8px) calc(100% - 8px), - 100% calc(100% - 8px), - 100% 8px, - calc(100% - 8px) 8px, - calc(100% - 8px) 0px, - 8px 0px, - 8px 8px, - 0px 8px - ); - box-sizing: border-box; - background-color: var(--theme-bg-secondary); - gap: 1rem; + display: flex; + flex-direction: column; + padding: 2.4rem; + overflow-wrap: break-word; + clip-path: polygon( + 0px calc(100% - 8px), + 8px calc(100% - 8px), + 8px 100%, + calc(100% - 8px) 100%, + calc(100% - 8px) calc(100% - 8px), + 100% calc(100% - 8px), + 100% 8px, + calc(100% - 8px) 8px, + calc(100% - 8px) 0px, + 8px 0px, + 8px 8px, + 0px 8px + ); + box-sizing: border-box; + background-color: var(--theme-bg-secondary); + gap: 1rem; + animation: fadeIn 0.4s ease-out; +} + +@keyframes fadeIn { + from { + opacity: 0; + transform: translateY(10px); + } + to { + opacity: 1; + transform: translateY(0); + } } .section, #header { - max-width: 36rem; - width: 100%; + max-width: 36rem; + width: 100%; } .alt { - color: var(--theme-fg-alt); + color: var(--theme-fg-alt); } -.alt-font { - font-size: 16px; - font-family: "Share Tech", monospace; - font-weight: 700; - letter-spacing: 0.05em; -} +/* .alt-font { */ +/* font-size: 16px; */ +/* font-family: "Gamja Flower", monospace; */ +/* font-weight: 700; */ +/* letter-spacing: 0.05em; */ +/* } */ .section p { - margin: 0; + margin: 0; } p { - hyphens: auto; -} - -.intro > .logo { - height: 1em; - margin-right: 0.5ch; - vertical-align: -4px; + -webkit-hyphens: auto; + hyphens: auto; } .centered { - display: flex; - flex-direction: column; - justify-content: center; - align-items: center; - width: 100%; - height: 100%; - gap: 1em; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + width: 100%; + height: 100%; + gap: 1em; } .title { - color: var(--theme-accent-title); - padding-bottom: 0.25em; - border-bottom: 2px solid var(--theme-border); - text-align: center; - justify-content: center; - margin: 0px; - padding-bottom: 0.5rem; - display: flex; - align-items: center; - gap: 0.25em; + color: var(--theme-accent-title); + padding-bottom: 0.25em; + border-bottom: 2px solid var(--theme-border); + text-align: center; + justify-content: center; + margin: 0px; + padding-bottom: 0.5rem; + display: flex; + align-items: center; + gap: 0.25em; } .title-1 { - font-size: 1.25rem; + font-size: 1.25rem; } -.title-3 { - color: var(--theme-fg-alt); - padding-bottom: 0.16rem; - border-bottom: none; - font-size: 0.9rem; +.title-2 { + color: var(--theme-fg-alt); + padding-bottom: 0.16rem; + border-bottom: none; + font-size: 0.9rem; } -.title.title-3::after { - background-image: linear-gradient( - 90deg, - rgba(var(--theme-border-rgb), 0) 0, - rgba(var(--theme-fg-alt-rgb), 255) - ); +.title.title-2::after { + background-image: linear-gradient( + 90deg, + rgba(var(--theme-border-rgb), 0) 0, + rgba(var(--theme-fg-alt-rgb), 255) + ); } .title::after { - background-image: linear-gradient( - 90deg, - rgba(var(--theme-accent-title-rgb), 0) 0, - rgba(var(--theme-accent-title-rgb), 255) - ); - content: ""; - display: block; - flex: 1; - margin-left: 0.5ch; - height: 0.3rem; - border-radius: 3px; + background-image: linear-gradient( + 90deg, + rgba(var(--theme-accent-title-rgb), 0) 0, + rgba(var(--theme-accent-title-rgb), 255) + ); + content: ""; + display: block; + flex: 1; + margin-left: 0.5ch; + height: 0.3rem; + border-radius: 3px; } .title .icon { - height: 24px; - width: 24px; - shape-rendering: crispEdges; + height: 24px; + width: 24px; + shape-rendering: crispEdges; } .box { - background-color: var(--theme-accent-alt); - color: var(--theme-accent); - padding: 0.5rem 1rem; - line-height: 1; - box-sizing: border-box; - width: fit-content; - overflow: hidden; - text-overflow: ellipsis; - word-break: break-all; - clip-path: polygon( - 0px calc(100% - 6px), - 6px calc(100% - 6px), - 6px 100%, - calc(100% - 6px) 100%, - calc(100% - 6px) calc(100% - 6px), - 100% calc(100% - 6px), - 100% 6px, - calc(100% - 6px) 6px, - calc(100% - 6px) 0px, - 6px 0px, - 6px 6px, - 0px 6px - ); - display: flex; - align-items: center; - gap: 0.35rem; + background-color: var(--theme-accent-alt); + color: var(--theme-accent); + padding: 0.5rem 1rem; + line-height: 1; + box-sizing: border-box; + width: fit-content; + overflow: hidden; + text-overflow: ellipsis; + word-break: break-all; + clip-path: polygon( + 0px calc(100% - 6px), + 6px calc(100% - 6px), + 6px 100%, + calc(100% - 6px) 100%, + calc(100% - 6px) calc(100% - 6px), + 100% calc(100% - 6px), + 100% 6px, + calc(100% - 6px) 6px, + calc(100% - 6px) 0px, + 6px 0px, + 6px 6px, + 0px 6px + ); + display: flex; + align-items: center; + gap: 0.35rem; + transition: filter 0.2s ease; +} + +.box:hover { + filter: brightness(120%); +} + +.box svg { + flex-shrink: 0; } a, a:visited, a:active { - color: var(--theme-accent-third); - text-decoration: none; + color: var(--theme-accent-third); + text-decoration: none; } a:hover { - text-decoration: 2px underline; - text-underline-offset: 3px; + text-decoration: 2px underline; + text-underline-offset: 3px; } #header { - position: relative; - display: flex; - flex-direction: row; - justify-content: space-between; - z-index: 1; + position: relative; + display: flex; + flex-direction: row; + justify-content: space-between; + z-index: 1; } #header, #header #left, #header #links { - flex-wrap: wrap; + flex-wrap: wrap; } #header #left, #header #links { - display: flex; - flex-direction: row; - align-items: center; - gap: 0.5rem; - margin: 0; - padding: 0; + display: flex; + flex-direction: row; + align-items: center; + gap: 0.5rem; + margin: 0; + padding: 0; } #header img { - width: auto; - height: 1.5em; + width: auto; + height: 1.5em; } nav .module { - clip-path: polygon( - 0px calc(100% - 6px), - 6px calc(100% - 6px), - 6px 100%, - calc(100% - 6px) 100%, - calc(100% - 6px) calc(100% - 6px), - 100% calc(100% - 6px), - 100% 6px, - calc(100% - 6px) 6px, - calc(100% - 6px) 0px, - 6px 0px, - 6px 6px, - 0px 6px - ); - padding: 0.5em 1.25em; - box-sizing: border-box; - background-color: var(--theme-bg-secondary); + clip-path: polygon( + 0px calc(100% - 6px), + 6px calc(100% - 6px), + 6px 100%, + calc(100% - 6px) 100%, + calc(100% - 6px) calc(100% - 6px), + 100% calc(100% - 6px), + 100% 6px, + calc(100% - 6px) 6px, + calc(100% - 6px) 0px, + 6px 0px, + 6px 6px, + 0px 6px + ); + padding: 0.5em 1.25em; + box-sizing: border-box; + background-color: var(--theme-bg-secondary); } #logo { - display: flex; - align-items: center; - padding: 0.5em; + display: flex; + align-items: center; + padding: 0.5em; } .labeled-icons { - margin: 0; - padding: 0; - list-style: none; - display: flex; - flex-direction: row; - flex-wrap: wrap; - gap: 0.35rem; - margin-top: 0.25rem; + margin: 0; + padding: 0; + list-style: none; + display: flex; + flex-direction: row; + flex-wrap: wrap; + gap: 0.35rem; + margin-top: 0.25rem; } .labeled-icons.full { - flex-direction: column; - & > * { - flex-grow: 1; - } - & .box { - width: 100%; - } + flex-direction: column; + & > * { + flex-grow: 1; + } + & .box { + width: 100%; + } } .labeled-icons.b88x31 { - gap: 0.35rem; + gap: 0.35rem; } .labeled-icons.b88x31 a { - display: flex; - width: 88px; - height: 31px; - background: var(--theme-bg-tertiary); - color: var(--theme-fg-alt); - justify-content: center; + display: flex; + width: 88px; + height: 31px; + background: var(--theme-bg-tertiary); + color: var(--theme-fg-alt); + justify-content: center; } .labeled-icons.b88x31 a, #muxiepuff { - clip-path: polygon( - 0px calc(100% - 3px), - 3px calc(100% - 3px), - 3px 100%, - calc(100% - 3px) 100%, - calc(100% - 3px) calc(100% - 3px), - 100% calc(100% - 3px), - 100% 3px, - calc(100% - 3px) 3px, - calc(100% - 3px) 0px, - 3px 0px, - 3px 3px, - 0px 3px - ); + clip-path: polygon( + 0px calc(100% - 3px), + 3px calc(100% - 3px), + 3px 100%, + calc(100% - 3px) 100%, + calc(100% - 3px) calc(100% - 3px), + 100% calc(100% - 3px), + 100% 3px, + calc(100% - 3px) 3px, + calc(100% - 3px) 0px, + 3px 0px, + 3px 3px, + 0px 3px + ); } .labeled-icons.b88x31 li { - transition: filter 0.2s ease; + transition: filter 0.2s ease; } .labeled-icons.b88x31 li:hover { - filter: brightness(0.5); + filter: brightness(0.5); } .labeled-icons.b88x31 img { - image-rendering: pixelated; + image-rendering: pixelated; } .labeled-icons.b88x31 img { - image-rendering: pixelated; + image-rendering: pixelated; } #extra { - display: flex; - list-style: none; - flex-wrap: wrap; - align-items: center; - justify-content: space-between; - padding: 0; + display: flex; + list-style: none; + flex-wrap: wrap; + align-items: center; + justify-content: space-between; + padding: 0; } #extra svg { - width: 15rem; - max-width: 100%; - height: 15rem; + width: 15rem; + max-width: 100%; + height: 15rem; } @media (max-width: 768px) { - html { - font-size: 18px; - } + html { + font-size: 18px; + } + .section { + padding: 1.5rem; + } + #extra { + justify-content: center; + gap: 1rem; + } + + #extra svg { + width: 8rem; + height: 8rem; + } } @media (max-width: 480px) { - html { - font-size: 16px; - } + html { + font-size: 16px; + } - body { - padding: 8px; - } -} + body { + padding: 8px; + } -@media (max-width: 768px) { - .section { - padding: 1.5rem; - } -} + .section { + padding: 1rem; + } -@media (max-width: 480px) { - .section { - padding: 1rem; - } -} + .box { + padding: 0.4rem 0.8rem; + font-size: 0.9em; + } -@media (max-width: 480px) { - .box { - padding: 0.4rem 0.8rem; - font-size: 0.9em; - } + .box svg { + width: 20px; + height: 20px; + } - .box svg { - width: 20px; - height: 20px; - } -} + .title { + font-size: 1.1rem; + } -@media (max-width: 480px) { - .title { - font-size: 1.1rem; - } -} + #extra svg { + width: 6rem; + height: 6rem; + } -@media (max-width: 768px) { - #extra { - justify-content: center; - gap: 1rem; - } - - #extra svg { - width: 8rem; - height: 8rem; - } -} - -@media (max-width: 480px) { - #extra svg { - width: 6rem; - height: 6rem; - } -} - -@media (max-width: 480px) { - .alt-font { - font-size: 14px; - } + .alt-font { + font-size: 14px; + } } #muxiepuff { - display: flex; - padding: 0; - background: none; - outline: none; - border: none; - cursor: pointer; + display: flex; + padding: 0; + background: none; + outline: none; + border: none; + cursor: pointer; } .name-scroller { - display: inline-block; - position: relative; - vertical-align: middle; - margin-top: -3px; - overflow: hidden; + display: inline-block; + position: relative; + vertical-align: middle; + margin-top: -3px; + overflow: hidden; } .name-wrapper { - transition: - transform 0.3s ease-in-out, - opacity 0.3s ease-in-out; - transform: translateY(0); - opacity: 1; + transition: transform 0.3s ease-in-out, opacity 0.3s ease-in-out; + transform: translateY(0); + opacity: 1; } .name-wrapper.animating { - transform: translateY(-100%); - opacity: 0; + transform: translateY(-100%); + opacity: 0; } .name-wrapper:not(.animating) { - transform: translateY(0); - opacity: 1; + transform: translateY(0); + opacity: 1; } .name-text { - position: relative; - display: inline-block; + position: relative; + display: inline-block; } .name-underline { - position: absolute; - bottom: 2px; - left: 0; - width: 100%; - height: 2px; - background: currentColor; - box-shadow: 0 0 8px currentColor; - animation: pulse 2s ease-in-out infinite; + position: absolute; + bottom: 2px; + left: 0; + width: 100%; + height: 2px; + background: currentColor; + box-shadow: 0 0 8px currentColor; + animation: pulse 2s ease-in-out infinite; } @keyframes pulse { - 0%, - 100% { - opacity: 1; - } - 50% { - opacity: 0.5; - } + 0%, + 100% { + opacity: 1; + } + 50% { + opacity: 0.5; + } } .inline-code { - font-family: "Share Tech Mono", "Share Tech", monospace; - font-size: 0.75em; - background-color: var(--theme-border); - padding: 1px; - display: inline-block; - clip-path: polygon( - 0px calc(100% - 2px), - 2px calc(100% - 2px), - 2px 100%, - calc(100% - 2px) 100%, - calc(100% - 2px) calc(100% - 2px), - 100% calc(100% - 2px), - 100% 2px, - calc(100% - 2px) 2px, - calc(100% - 2px) 0px, - 2px 0px, - 2px 2px, - 0px 2px - ); - line-height: 1.6; - vertical-align: middle; + font-family: monospace, "Jersey 15", sans-serif; + font-size: 0.75em; + background-color: var(--theme-border); + padding: 1px; + display: inline-block; + clip-path: polygon( + 0px calc(100% - 2px), + 2px calc(100% - 2px), + 2px 100%, + calc(100% - 2px) 100%, + calc(100% - 2px) calc(100% - 2px), + 100% calc(100% - 2px), + 100% 2px, + calc(100% - 2px) 2px, + calc(100% - 2px) 0px, + 2px 0px, + 2px 2px, + 0px 2px + ); + line-height: 1.6; + vertical-align: middle; + cursor: pointer; + outline: none; + border: none; } .inline-code > span { - display: block; - background-color: var(--theme-bg-tertiary); - color: var(--theme-fg); - padding: 0.15em 0.4em; - clip-path: polygon( - 0px calc(100% - 2px), - 2px calc(100% - 2px), - 2px 100%, - calc(100% - 2px) 100%, - calc(100% - 2px) calc(100% - 2px), - 100% calc(100% - 2px), - 100% 2px, - calc(100% - 2px) 2px, - calc(100% - 2px) 0px, - 2px 0px, - 2px 2px, - 0px 2px - ); - font-weight: 500; + display: block; + background-color: var(--theme-bg-tertiary); + color: var(--theme-fg); + padding: 0.15em 0.4em; + clip-path: polygon( + 0px calc(100% - 2px), + 2px calc(100% - 2px), + 2px 100%, + calc(100% - 2px) 100%, + calc(100% - 2px) calc(100% - 2px), + 100% calc(100% - 2px), + 100% 2px, + calc(100% - 2px) 2px, + calc(100% - 2px) 0px, + 2px 0px, + 2px 2px, + 0px 2px + ); + font-weight: 500; } p, .title { - transform: translateZ(0px); - -webkit-transform: translateZ(0px); - will-change: transform; + transform: translateZ(0px); + -webkit-transform: translateZ(0px); + will-change: transform; } hr { - width: 100%; - border: none; - border-top: 2px solid var(--theme-border); + width: 100%; + border: none; + border-top: 2px solid var(--theme-border); } footer.section { - display: flex; - align-items: center; - justify-content: center; - gap: 1ch; + display: flex; + align-items: center; + justify-content: center; + gap: 1ch; } footer p { - text-align: center; - gap: 1ch; - margin: 0; - padding: 0; + text-align: center; + gap: 1ch; + margin: 0; + padding: 0; } footer a { - overflow-wrap: anywhere; - word-break: break-word; + overflow-wrap: anywhere; + word-break: break-word; } footer #f-heart { - color: var(--theme-accent-third); - vertical-align: middle; + color: var(--theme-accent-third); + vertical-align: middle; } diff --git a/components/Box.tsx b/components/Box.tsx index 11c2d53..66b0e2d 100644 --- a/components/Box.tsx +++ b/components/Box.tsx @@ -7,16 +7,16 @@ import { ComponentChildren, h } from "preact"; import { LinkIcon } from "./Icon.tsx"; interface PolygonBoxProps { - as?: keyof HTMLElementTagNameMap; - children?: ComponentChildren; - [key: string]: any; + as?: keyof HTMLElementTagNameMap; + children?: ComponentChildren; + [key: string]: any; } export default function Box( - { as: Tag = "span", class: className, children, ...props }: PolygonBoxProps, + { as: Tag = "span", class: className, children, ...props }: PolygonBoxProps, ) { - const content = Tag === "a" - ? [children, h(LinkIcon, { size: 16, class: "link-icon" })] - : children; - return h(Tag, { class: `box ${className || ""}`, ...props }, content); + const content = Tag === "a" + ? [children, h(LinkIcon, { size: 16, class: "link-icon" })] + : children; + return h(Tag, { class: `box ${className || ""}`, ...props }, content); } diff --git a/components/Footer.tsx b/components/Footer.tsx index 4d81208..5855baa 100644 --- a/components/Footer.tsx +++ b/components/Footer.tsx @@ -7,23 +7,25 @@ import { HeartFilledIcon } from "./Icon.tsx"; import Link from "./Link.tsx"; export default function Footer() { - return ( - - ); + return ( + + ); } diff --git a/components/Header.tsx b/components/Header.tsx index 3c71fb1..c6fbc66 100644 --- a/components/Header.tsx +++ b/components/Header.tsx @@ -4,32 +4,38 @@ */ export default function Header() { - return ( - - ); + return ( + + ); } diff --git a/components/Icon.tsx b/components/Icon.tsx index cff513b..8cd4823 100644 --- a/components/Icon.tsx +++ b/components/Icon.tsx @@ -4,439 +4,455 @@ */ interface IconProps { - size?: number; - [key: string]: any; + size?: number; + [key: string]: any; } export function LinkIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function PersonIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function HeartIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function CommentIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function AttachmentIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function LabelIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function DiscordIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function SignalIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function LastfmIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function BlueskyIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function MastodonIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function ForgejoIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function CodebergIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function GitHubIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function MailIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function TwitterIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function KofiIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function BitcoinIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function BitcoinCashIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function MoneroIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function NanoIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function LitecoinIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function EthereumIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); } export function HeartFilledIcon({ size = 24, ...props }: IconProps) { - return ( - - ); + return ( + + ); +} + +export function GitHubSponsorsIcon({ size = 24, ...props }: IconProps) { + return ( + + ); } diff --git a/components/Layout.tsx b/components/Layout.tsx index 1d1e586..e169fc4 100644 --- a/components/Layout.tsx +++ b/components/Layout.tsx @@ -6,27 +6,27 @@ import { ComponentChildren } from "preact"; interface LayoutProps { - children: ComponentChildren; - [key: string]: any; + children: ComponentChildren; + [key: string]: any; } export function Section({ children, class: className, ...props }: LayoutProps) { - return ( -
- {children}
-
- );
+ const handleClick = () => {
+ if (typeof children === "string") {
+ navigator.clipboard.writeText(children);
+ }
+ };
+ return (
+
+ );
}
diff --git a/islands/Meow.tsx b/islands/Meow.tsx
index 101786e..e34ef8b 100644
--- a/islands/Meow.tsx
+++ b/islands/Meow.tsx
@@ -4,16 +4,20 @@
*/
export default function Meow() {
- return (
-
- );
+ return (
+
+ );
}
diff --git a/islands/Name.tsx b/islands/Name.tsx
index 9e9ee88..3682f26 100644
--- a/islands/Name.tsx
+++ b/islands/Name.tsx
@@ -6,32 +6,31 @@
import { useEffect, useState } from "preact/hooks";
export default function Name() {
- const names = ["muxiepuff", "lívia", "aury"];
- const ipas = ["/ˈmuˌksipʌf/", "/li.vjɐ/", "/ˈaʊ̯ˌɾi/"]
- ;
- const [currentIndex, setCurrentIndex] = useState(0);
- const [isAnimating, setIsAnimating] = useState(false);
+ const names = ["muxiepuff", "lívia", "aury"];
+ const ipas = ["/ˈmuˌksipʌf/", "/li.vjɐ/", "/ˈaʊ̯ˌɾi/"];
+ const [currentIndex, setCurrentIndex] = useState(0);
+ const [isAnimating, setIsAnimating] = useState(false);
- useEffect(() => {
- const interval = setInterval(() => {
- setIsAnimating(true);
- setTimeout(() => {
- setCurrentIndex((prev) => (prev + 1) % names.length);
- setIsAnimating(false);
- }, 200);
- }, 2000);
- return () => clearInterval(interval);
- }, []);
+ useEffect(() => {
+ const interval = setInterval(() => {
+ setIsAnimating(true);
+ setTimeout(() => {
+ setCurrentIndex((prev) => (prev + 1) % names.length);
+ setIsAnimating(false);
+ }, 200);
+ }, 2000);
+ return () => clearInterval(interval);
+ }, []);
- return (
-
-
-
- {names[currentIndex]}
-
- {" "}
- ({ipas[currentIndex]}, she/her)
-
-
- );
+ return (
+
+
+
+ {names[currentIndex]}
+
+ {" "}
+ ({ipas[currentIndex]}, she/her)
+
+
+ );
}
diff --git a/islands/Rain.tsx b/islands/Rain.tsx
index e7a26de..4bc8f86 100644
--- a/islands/Rain.tsx
+++ b/islands/Rain.tsx
@@ -6,41 +6,41 @@
import { useEffect, useRef } from "preact/hooks";
interface Particle {
- offsetX: number;
- offsetY: number;
- velocityX: number;
- velocityY: number;
+ offsetX: number;
+ offsetY: number;
+ velocityX: number;
+ velocityY: number;
}
interface Raindrop {
- x: number;
- y: number;
- length: number;
- speed: number;
- opacity: number;
- floorHeight: number;
+ x: number;
+ y: number;
+ length: number;
+ speed: number;
+ opacity: number;
+ floorHeight: number;
}
interface Splash {
- x: number;
- y: number;
- age: number;
- maxAge: number;
- particles: Particle[];
+ x: number;
+ y: number;
+ age: number;
+ maxAge: number;
+ particles: Particle[];
}
interface Star {
- x: number;
- y: number;
- size: number;
- brightness: number;
- twinkleSpeed: number;
- twinkleOffset: number;
- age: number;
- maxAge: number;
- canVanish: boolean;
- color: { r: number; g: number; b: number };
- colorIndex: number;
+ x: number;
+ y: number;
+ size: number;
+ brightness: number;
+ twinkleSpeed: number;
+ twinkleOffset: number;
+ age: number;
+ maxAge: number;
+ canVanish: boolean;
+ color: { r: number; g: number; b: number };
+ colorIndex: number;
}
const PIXEL_SIZE = 4;
@@ -82,293 +82,299 @@ const ALPHA_STAR_COLORS: string[] = [];
const NUM_COLOR_VARIATIONS = 20;
for (let i = 0; i < NUM_COLOR_VARIATIONS; i++) {
- const brightness = 1 - (i / NUM_COLOR_VARIATIONS) * 0.5;
- RAIN_COLORS.push(
- `rgb(${Math.floor(RAIN_COLOR.r * brightness)},${
- Math.floor(RAIN_COLOR.g * brightness)
- },${Math.floor(RAIN_COLOR.b * brightness)})`,
- );
+ const brightness = 1 - (i / NUM_COLOR_VARIATIONS) * 0.5;
+ RAIN_COLORS.push(
+ `rgb(${Math.floor(RAIN_COLOR.r * brightness)},${
+ Math.floor(
+ RAIN_COLOR.g * brightness,
+ )
+ },${Math.floor(RAIN_COLOR.b * brightness)})`,
+ );
}
for (let i = 0; i < NUM_COLOR_VARIATIONS; i++) {
- const brightness = STAR_BRIGHTNESS_MIN +
- (i / NUM_COLOR_VARIATIONS) * STAR_BRIGHTNESS_RANGE;
- STAR_COLORS.push(
- `rgb(${Math.floor(RAIN_COLOR.r * brightness)},${
- Math.floor(RAIN_COLOR.g * brightness)
- },${Math.floor(RAIN_COLOR.b * brightness)})`,
- );
- ALPHA_STAR_COLORS.push(
- `rgba(${Math.floor(RAIN_COLOR.r * brightness)},${
- Math.floor(RAIN_COLOR.g * brightness)
- },${Math.floor(RAIN_COLOR.b * brightness)},`,
- );
+ const brightness = STAR_BRIGHTNESS_MIN +
+ (i / NUM_COLOR_VARIATIONS) * STAR_BRIGHTNESS_RANGE;
+ STAR_COLORS.push(
+ `rgb(${Math.floor(RAIN_COLOR.r * brightness)},${
+ Math.floor(
+ RAIN_COLOR.g * brightness,
+ )
+ },${Math.floor(RAIN_COLOR.b * brightness)})`,
+ );
+ ALPHA_STAR_COLORS.push(
+ `rgba(${Math.floor(RAIN_COLOR.r * brightness)},${
+ Math.floor(
+ RAIN_COLOR.g * brightness,
+ )
+ },${Math.floor(RAIN_COLOR.b * brightness)},`,
+ );
}
const SPLASH_PARTICLE_COLOR = `rgb(${RAIN_COLOR.r * 0.9},${
- RAIN_COLOR.g * 0.95
+ RAIN_COLOR.g * 0.95
},${RAIN_COLOR.b * 1.1})`;
const SPLASH_RIPPLE_COLOR = `rgb(${RAIN_COLOR.r * 0.7},${RAIN_COLOR.g * 0.8},${
- RAIN_COLOR.b * 0.95
+ RAIN_COLOR.b * 0.95
})`;
function createParticle(): Particle {
- return {
- offsetX: (Math.random() - 0.5) * PARTICLE_OFFSET_RANGE,
- offsetY: -(Math.random() * PARTICLE_Y_RANGE + PARTICLE_Y_MIN),
- velocityX: (Math.random() - 0.5) * PARTICLE_VX_RANGE,
- velocityY: -(Math.random() * PARTICLE_VY_RANGE + PARTICLE_VY_MIN),
- };
+ return {
+ offsetX: (Math.random() - 0.5) * PARTICLE_OFFSET_RANGE,
+ offsetY: -(Math.random() * PARTICLE_Y_RANGE + PARTICLE_Y_MIN),
+ velocityX: (Math.random() - 0.5) * PARTICLE_VX_RANGE,
+ velocityY: -(Math.random() * PARTICLE_VY_RANGE + PARTICLE_VY_MIN),
+ };
}
function createSplash(x: number, y: number): Splash {
- return {
- x,
- y,
- age: 0,
- maxAge: 12,
- particles: Array.from(
- { length: Math.floor(Math.random() * 3) + 3 },
- createParticle,
- ),
- };
+ return {
+ x,
+ y,
+ age: 0,
+ maxAge: 12,
+ particles: Array.from(
+ { length: Math.floor(Math.random() * 3) + 3 },
+ createParticle,
+ ),
+ };
}
function createRaindrop(gridWidth: number, gridHeight: number): Raindrop {
- const bias = Math.random() * Math.random();
- return {
- x: Math.floor(Math.random() * gridWidth),
- y: Math.floor(Math.random() * gridHeight) - gridHeight,
- length: Math.floor(Math.random() * 4) + 2,
- speed: RAIN_SPEED * (Math.random() * 2 + 1),
- opacity: Math.random() * 0.3 + 0.6,
- floorHeight: window.innerHeight *
- (FLOOR_HEIGHT_MIN + bias * FLOOR_HEIGHT_RANGE),
- };
+ const bias = Math.random() * Math.random();
+ return {
+ x: Math.floor(Math.random() * gridWidth),
+ y: Math.floor(Math.random() * gridHeight) - gridHeight,
+ length: Math.floor(Math.random() * 4) + 2,
+ speed: RAIN_SPEED * (Math.random() * 2 + 1),
+ opacity: Math.random() * 0.3 + 0.6,
+ floorHeight: window.innerHeight *
+ (FLOOR_HEIGHT_MIN + bias * FLOOR_HEIGHT_RANGE),
+ };
}
function createStar(): Star {
- const brightness = Math.random() * STAR_BRIGHTNESS_RANGE +
- STAR_BRIGHTNESS_MIN;
- const colorIndex = Math.floor(
- (brightness - STAR_BRIGHTNESS_MIN) / STAR_BRIGHTNESS_RANGE *
- (NUM_COLOR_VARIATIONS - 1),
- );
+ const brightness = Math.random() * STAR_BRIGHTNESS_RANGE +
+ STAR_BRIGHTNESS_MIN;
+ const colorIndex = Math.floor(
+ ((brightness - STAR_BRIGHTNESS_MIN) / STAR_BRIGHTNESS_RANGE) *
+ (NUM_COLOR_VARIATIONS - 1),
+ );
- return {
- x: Math.random() * window.innerWidth,
- y: Math.random() * window.innerHeight,
- size: Math.random() > STAR_LARGE_THRESHOLD
- ? STAR_SIZE_LARGE
- : STAR_SIZE_SMALL,
- twinkleSpeed: Math.random() * STAR_TWINKLE_RANGE + STAR_TWINKLE_MIN,
- twinkleOffset: Math.random() * Math.PI * 2,
- brightness,
- age: 0,
- maxAge: STAR_AGE_MIN + Math.random() * STAR_AGE_RANGE,
- canVanish: false,
- colorIndex,
- };
+ return {
+ x: Math.random() * window.innerWidth,
+ y: Math.random() * window.innerHeight,
+ size: Math.random() > STAR_LARGE_THRESHOLD
+ ? STAR_SIZE_LARGE
+ : STAR_SIZE_SMALL,
+ twinkleSpeed: Math.random() * STAR_TWINKLE_RANGE + STAR_TWINKLE_MIN,
+ twinkleOffset: Math.random() * Math.PI * 2,
+ brightness,
+ age: 0,
+ maxAge: STAR_AGE_MIN + Math.random() * STAR_AGE_RANGE,
+ canVanish: false,
+ colorIndex,
+ };
}
function updateParticle(p: Particle) {
- p.offsetX += p.velocityX;
- p.offsetY += p.velocityY;
- p.velocityY += PARTICLE_GRAVITY;
+ p.offsetX += p.velocityX;
+ p.offsetY += p.velocityY;
+ p.velocityY += PARTICLE_GRAVITY;
}
function updateSplash(splash: Splash) {
- splash.age++;
- splash.particles.forEach(updateParticle);
+ splash.age++;
+ splash.particles.forEach(updateParticle);
}
function updateRaindrop(
- drop: Raindrop,
- splashes: Splash[],
- gridWidth: number,
- gridHeight: number,
+ drop: Raindrop,
+ splashes: Splash[],
+ gridWidth: number,
+ gridHeight: number,
) {
- drop.y += drop.speed;
- const dropY = drop.y * PIXEL_SIZE;
+ drop.y += drop.speed;
+ const dropY = drop.y * PIXEL_SIZE;
- if (dropY >= drop.floorHeight && Math.random() < SPLASH_CHANCE) {
- splashes.push(createSplash(drop.x, Math.floor(dropY / PIXEL_SIZE)));
- Object.assign(drop, createRaindrop(gridWidth, gridHeight));
- } else if (dropY > window.innerHeight) {
- Object.assign(drop, createRaindrop(gridWidth, gridHeight));
- }
+ if (dropY >= drop.floorHeight && Math.random() < SPLASH_CHANCE) {
+ splashes.push(createSplash(drop.x, Math.floor(dropY / PIXEL_SIZE)));
+ Object.assign(drop, createRaindrop(gridWidth, gridHeight));
+ } else if (dropY > window.innerHeight) {
+ Object.assign(drop, createRaindrop(gridWidth, gridHeight));
+ }
}
function updateStar(star: Star): void {
- star.age++;
- const phase = (star.age * star.twinkleSpeed + star.twinkleOffset) %
- (2 * Math.PI);
+ star.age++;
+ const phase = (star.age * star.twinkleSpeed + star.twinkleOffset) %
+ (2 * Math.PI);
- if (!star.canVanish && phase < star.twinkleSpeed) {
- star.canVanish = true;
- }
+ if (!star.canVanish && phase < star.twinkleSpeed) {
+ star.canVanish = true;
+ }
- if (star.canVanish && star.age > star.maxAge) {
- Object.assign(star, createStar());
- } else if (star.x > window.innerWidth || star.y > window.innerHeight) {
- Object.assign(star, createStar());
- }
+ if (star.canVanish && star.age > star.maxAge) {
+ Object.assign(star, createStar());
+ } else if (star.x > window.innerWidth || star.y > window.innerHeight) {
+ Object.assign(star, createStar());
+ }
}
const isSplashDead = (splash: Splash): boolean => splash.age >= splash.maxAge;
const drawRaindrop = (ctx: CanvasRenderingContext2D, drop: Raindrop): void => {
- ctx.globalAlpha = drop.opacity;
- const xPos = drop.x * PIXEL_SIZE;
+ ctx.globalAlpha = drop.opacity;
+ const xPos = drop.x * PIXEL_SIZE;
- for (let i = 0; i < drop.length; i++) {
- const colorIndex = Math.floor(
- (i / drop.length) * NUM_COLOR_VARIATIONS * 0.5,
- );
- ctx.fillStyle = RAIN_COLORS[colorIndex];
- ctx.fillRect(xPos, (drop.y - i) * PIXEL_SIZE, PIXEL_SIZE, PIXEL_SIZE);
- }
- ctx.globalAlpha = 1;
+ for (let i = 0; i < drop.length; i++) {
+ const colorIndex = Math.floor(
+ (i / drop.length) * NUM_COLOR_VARIATIONS * 0.5,
+ );
+ ctx.fillStyle = RAIN_COLORS[colorIndex];
+ ctx.fillRect(xPos, (drop.y - i) * PIXEL_SIZE, PIXEL_SIZE, PIXEL_SIZE);
+ }
+ ctx.globalAlpha = 1;
};
function drawSplash(ctx: CanvasRenderingContext2D, splash: Splash) {
- if (splash.age >= splash.maxAge) return;
+ if (splash.age >= splash.maxAge) return;
- const progress = splash.age / splash.maxAge;
- ctx.globalAlpha = 1 - progress;
+ const progress = splash.age / splash.maxAge;
+ ctx.globalAlpha = 1 - progress;
- ctx.fillStyle = SPLASH_PARTICLE_COLOR;
- splash.particles.forEach((p) => {
- const px = Math.floor(splash.x + p.offsetX) * PIXEL_SIZE;
- const py = Math.floor(splash.y + p.offsetY) * PIXEL_SIZE;
- ctx.fillRect(px, py, PIXEL_SIZE, PIXEL_SIZE);
- });
+ ctx.fillStyle = SPLASH_PARTICLE_COLOR;
+ splash.particles.forEach((p) => {
+ const px = Math.floor(splash.x + p.offsetX) * PIXEL_SIZE;
+ const py = Math.floor(splash.y + p.offsetY) * PIXEL_SIZE;
+ ctx.fillRect(px, py, PIXEL_SIZE, PIXEL_SIZE);
+ });
- if (splash.age < 6) {
- const rippleSize = Math.floor(splash.age / 2) + 1;
- const splashY = splash.y * PIXEL_SIZE;
- ctx.fillStyle = SPLASH_RIPPLE_COLOR;
- ctx.fillRect(
- (splash.x - rippleSize) * PIXEL_SIZE,
- splashY,
- PIXEL_SIZE,
- PIXEL_SIZE,
- );
- ctx.fillRect(
- (splash.x + rippleSize) * PIXEL_SIZE,
- splashY,
- PIXEL_SIZE,
- PIXEL_SIZE,
- );
- }
+ if (splash.age < 6) {
+ const rippleSize = Math.floor(splash.age / 2) + 1;
+ const splashY = splash.y * PIXEL_SIZE;
+ ctx.fillStyle = SPLASH_RIPPLE_COLOR;
+ ctx.fillRect(
+ (splash.x - rippleSize) * PIXEL_SIZE,
+ splashY,
+ PIXEL_SIZE,
+ PIXEL_SIZE,
+ );
+ ctx.fillRect(
+ (splash.x + rippleSize) * PIXEL_SIZE,
+ splashY,
+ PIXEL_SIZE,
+ PIXEL_SIZE,
+ );
+ }
- ctx.globalAlpha = 1;
+ ctx.globalAlpha = 1;
}
-function drawStar(
- ctx: CanvasRenderingContext2D,
- star: Star,
- time: number,
-) {
- const twinkle = Math.sin(time * star.twinkleSpeed + star.twinkleOffset) *
- STAR_TWINKLE_AMPLITUDE + STAR_TWINKLE_BASE;
- const alpha = star.brightness * twinkle;
+function drawStar(ctx: CanvasRenderingContext2D, star: Star, time: number) {
+ const twinkle = Math.sin(time * star.twinkleSpeed + star.twinkleOffset) *
+ STAR_TWINKLE_AMPLITUDE +
+ STAR_TWINKLE_BASE;
+ const alpha = star.brightness * twinkle;
- const x = Math.floor(star.x);
- const y = Math.floor(star.y);
- const color = ALPHA_STAR_COLORS[star.colorIndex];
+ const x = Math.floor(star.x);
+ const y = Math.floor(star.y);
+ const color = ALPHA_STAR_COLORS[star.colorIndex];
- ctx.fillStyle = color + alpha;
- ctx.fillRect(x, y, star.size, star.size);
+ ctx.fillStyle = color + alpha;
+ ctx.fillRect(x, y, star.size, star.size);
- if (star.size === STAR_SIZE_LARGE && alpha > STAR_GLOW_THRESHOLD) {
- ctx.fillStyle = color + (alpha * STAR_GLOW_ALPHA);
- ctx.fillRect(x - 4, y + 2, 4, 4);
- ctx.fillRect(x + 8, y + 2, 4, 4);
- ctx.fillRect(x + 2, y - 4, 4, 4);
- ctx.fillRect(x + 2, y + 8, 4, 4);
- }
+ if (star.size === STAR_SIZE_LARGE && alpha > STAR_GLOW_THRESHOLD) {
+ ctx.fillStyle = color + alpha * STAR_GLOW_ALPHA;
+ ctx.fillRect(x - 4, y + 2, 4, 4);
+ ctx.fillRect(x + 8, y + 2, 4, 4);
+ ctx.fillRect(x + 2, y - 4, 4, 4);
+ ctx.fillRect(x + 2, y + 8, 4, 4);
+ }
}
export default function Rain() {
- const canvasRef = useRef- - {" "} - muxiepuff{" "} - (/ˈmuˌksipʌf/, she/her) - {" · "} - - I'm just your average advocate for open access to information and - knowledge and aspiring electrical engineer with complementary - interests in systems programming and linguistics. Neurodivergent - student passionate about libre software. -
-- On the side, I maintain a modest FreeBSD server where I self-host - this website and various services. This is nothing particularly - impressive, just a growing curiosity about systems administration. - Service status and uptime available{" "} - here. -
- -
- Feel free to reach out through any of the platforms listed above.
- For email correspondence, you can reach me at{" "}
- base64 -d <<< bXV4QGFjcGkuYXQK.
-
- Psst! When discussing sensitive matters over email or other insecure
- communication channels, I'd really appreciate it if you could
- encrypt your message with my PGP key{" "}
- (fingerprint:{" "}
- AC14 9A39 5013 C572 CA74 8799 BCD2 117C 99E6 9817).
-
- If you enjoy throwing money at people on the internet, please - consider me! It keeps the server alive, fuels my tinkering with - esoteric technology, and helps me navigate some rough financial - patches and stay afloat while things are tight. -
-
+
+
+ uhhhhh g'day, name's laura and i go by she/her pronouns and i'm
+ a veeeery sweet autist{" "}
+ girl passionate about free software!! ty for your time ^^
+
+
+
+ Advocate for open access to information and knowledge and aspiring + electrical engineer with complementary interests in systems + programming and linguistics. +
++ On the side, I maintain a modest FreeBSD server where I self-host + this website and various services. This is nothing particularly + impressive, just a growing curiosity about systems administration. + Service status and uptime available{" "} + here. +
+ +
+ Feel free to reach out through any of the platforms listed above.
+ For email correspondence, you can reach me at{" "}
+ base64 -d <<< bXV4QGFjcGkuYXQK.
+
+ Psst! When discussing sensitive matters over email or other insecure
+ communication channels, I'd really appreciate it if you could
+ encrypt your message with my{" "}
+ PGP key (fingerprint:{" "}
+ AC14 9A39 5013 C572 CA74 8799 BCD2 117C 99E6 9817).
+
+ If you enjoy throwing money at people on the internet, please + consider me! It keeps the server alive, fuels my tinkering with + esoteric technology, and helps me navigate some rough financial + patches and stay afloat while things are tight. +
+