diff --git a/css/themes/frappe.css b/css/themes/frappe.css new file mode 100644 index 0000000..8b50d3e --- /dev/null +++ b/css/themes/frappe.css @@ -0,0 +1,38 @@ +/* Catppuccin Frappé — official palette + https://github.com/catppuccin/catppuccin (dark) */ +html[data-flavor="frappe"] { + color-scheme: dark; + + /* accents */ + --rosewater: #f2d5cf; + --flamingo: #eebebe; + --pink: #f4b8e4; + --accent-rgb: 244, 184, 228; /* live accent default = theme pink */ + --mauve: #ca9ee6; + --red: #e78284; + --maroon: #ea999c; + --peach: #ef9f76; + --yellow: #e5c890; + --green: #a6d189; + --teal: #81c8be; + --sky: #99d1db; + --saphire: #85c1dc; + --blue: #8caaee; + --lavender: #babbf1; + + /* text */ + --text: #c6d0f5; + --subtext-1: #b5bfe2; + --subtext-0: #a5adce; + --overlay-2: #949cbb; + --overlay-1: #838ba7; + --overlay-0: #737994; + + /* surfaces + backgrounds */ + --surface-2: #626880; + --surface-1: #51576d; + --surface-0: #414559; + --base: #303446; + --mantle: #292c3c; + --crust: #232634; +} \ No newline at end of file diff --git a/css/themes/latte.css b/css/themes/latte.css new file mode 100644 index 0000000..5935512 --- /dev/null +++ b/css/themes/latte.css @@ -0,0 +1,56 @@ +/* Catppuccin Latte — official palette + https://github.com/catppuccin/catppuccin (light) */ +html[data-flavor="latte"] { + color-scheme: light; + + /* accents */ + --rosewater: #dc8a78; + --flamingo: #dd7878; + --pink: #ea76cb; + --accent-rgb: 234, 118, 203; /* live accent default = theme pink */ + --mauve: #8839ef; + --red: #d20f39; + --maroon: #e64553; + --peach: #fe640b; + --yellow: #df8e1d; + --green: #40a02b; + --teal: #179299; + --sky: #04a5e5; + --saphire: #209fb5; + --blue: #1e66f5; + --lavender: #7287fd; + + /* text */ + --text: #4c4f69; + --subtext-1: #5c5f77; + --subtext-0: #6c6f85; + --overlay-2: #7c7f93; + --overlay-1: #8c8fa1; + --overlay-0: #9ca0b0; + + /* surfaces + backgrounds */ + --surface-2: #acb0be; + --surface-1: #bcc0cc; + --surface-0: #ccd0da; + --base: #eff1f5; + --mantle: #e6e9ef; + --crust: #dce0e8; +} + +/* Light-mode readability fixes (icons are white-filtered by default) */ +html[data-flavor="latte"] .icon { + filter: brightness(0); +} + +html[data-flavor="latte"] .link-card:hover .icon { + filter: none; +} + +html[data-flavor="latte"] .badge-icon { + filter: none; +} + +html[data-flavor="latte"] body::before { + filter: invert(14%) sepia(0.2) saturate(300%) hue-rotate(200deg) brightness(1.3); + opacity: 0.05; +} \ No newline at end of file diff --git a/css/themes/macchiato.css b/css/themes/macchiato.css new file mode 100644 index 0000000..b1e3a92 --- /dev/null +++ b/css/themes/macchiato.css @@ -0,0 +1,38 @@ +/* Catppuccin Macchiato — official palette + https://github.com/catppuccin/catppuccin (dark) */ +html[data-flavor="macchiato"] { + color-scheme: dark; + + /* accents */ + --rosewater: #f4dbd6; + --flamingo: #f0c6c6; + --pink: #f5bde6; + --accent-rgb: 245, 189, 230; /* live accent default = theme pink */ + --mauve: #c6a0f6; + --red: #ed8796; + --maroon: #ee99a0; + --peach: #f5a97f; + --yellow: #eed49f; + --green: #a6da95; + --teal: #8bd5ca; + --sky: #91d7e3; + --saphire: #7dc4e4; + --blue: #8aadf4; + --lavender: #b7bdf8; + + /* text */ + --text: #cad3f5; + --subtext-1: #b8c0e0; + --subtext-0: #a5adcb; + --overlay-2: #939ab7; + --overlay-1: #8087a2; + --overlay-0: #6e738d; + + /* surfaces + backgrounds */ + --surface-2: #5b6078; + --surface-1: #494d64; + --surface-0: #363a4f; + --base: #24273a; + --mantle: #1e2030; + --crust: #181926; +} \ No newline at end of file diff --git a/css/themes/mocha.css b/css/themes/mocha.css new file mode 100644 index 0000000..abe8b0a --- /dev/null +++ b/css/themes/mocha.css @@ -0,0 +1,38 @@ +/* Catppuccin Mocha — official palette + https://github.com/catppuccin/catppuccin (dark, default) */ +html[data-flavor="mocha"] { + color-scheme: dark; + + /* accents */ + --rosewater: #f5e0dc; + --flamingo: #f2cdcd; + --pink: #f5c2e7; + --accent-rgb: 245, 194, 231; /* live accent default = theme pink */ + --mauve: #cba6f7; + --red: #f38ba8; + --maroon: #eba0ac; + --peach: #fab387; + --yellow: #f9e2af; + --green: #a6e3a1; + --teal: #94e2d5; + --sky: #89dceb; + --saphire: #74c7ec; + --blue: #89b4fa; + --lavender: #b4befe; + + /* Text */ + --text: #cdd6f4; + --subtext-0: #a6adc8; + --subtext-1: #bac2de; + --overlay-0: #6c7086; + --overlay-1: #7f849c; + --overlay-2: #9399b2; + --surface-0: #313244; + --surface-1: #45475a; + --surface-2: #585b70; + + /* Backgrounds */ + --base: #1e1e2e; + --mantle: #181825; + --crust: #11111b; +} \ No newline at end of file diff --git a/images/favicon/avatar.png b/images/favicon/avatar.png new file mode 100644 index 0000000..d0c513a Binary files /dev/null and b/images/favicon/avatar.png differ diff --git a/images/favicon/favicon.png b/images/favicon/favicon.png new file mode 100644 index 0000000..e41bdba Binary files /dev/null and b/images/favicon/favicon.png differ diff --git a/images/favicon/favicon.svg b/images/favicon/favicon.svg new file mode 100644 index 0000000..f21a1a9 --- /dev/null +++ b/images/favicon/favicon.svg @@ -0,0 +1,20 @@ + diff --git a/images/misc/amd.svg b/images/misc/amd.svg new file mode 100644 index 0000000..ef0757c --- /dev/null +++ b/images/misc/amd.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/images/misc/apple.svg b/images/misc/apple.svg new file mode 100644 index 0000000..58235c3 --- /dev/null +++ b/images/misc/apple.svg @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/images/misc/arch.svg b/images/misc/arch.svg new file mode 100644 index 0000000..c622c5c --- /dev/null +++ b/images/misc/arch.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/images/misc/debian.svg b/images/misc/debian.svg new file mode 100644 index 0000000..55b6dd4 --- /dev/null +++ b/images/misc/debian.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/images/misc/oneko.gif b/images/misc/oneko.gif new file mode 100644 index 0000000..a009c2c Binary files /dev/null and b/images/misc/oneko.gif differ diff --git a/images/socials/bluesky.svg b/images/socials/bluesky.svg new file mode 100644 index 0000000..6361c05 --- /dev/null +++ b/images/socials/bluesky.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/images/socials/discord.svg b/images/socials/discord.svg new file mode 100644 index 0000000..5bae97e --- /dev/null +++ b/images/socials/discord.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/images/socials/email.svg b/images/socials/email.svg new file mode 100644 index 0000000..243173c --- /dev/null +++ b/images/socials/email.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/images/socials/git-gay.svg b/images/socials/git-gay.svg new file mode 100644 index 0000000..cc12cd9 --- /dev/null +++ b/images/socials/git-gay.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/images/socials/github.svg b/images/socials/github.svg new file mode 100644 index 0000000..ed0408e --- /dev/null +++ b/images/socials/github.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/images/socials/linkedin.svg b/images/socials/linkedin.svg new file mode 100644 index 0000000..acc47ab --- /dev/null +++ b/images/socials/linkedin.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/images/socials/mastodon.svg b/images/socials/mastodon.svg new file mode 100644 index 0000000..4ca42f3 --- /dev/null +++ b/images/socials/mastodon.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/images/socials/reddit.svg b/images/socials/reddit.svg new file mode 100644 index 0000000..5ceddfa --- /dev/null +++ b/images/socials/reddit.svg @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/images/socials/site.svg b/images/socials/site.svg new file mode 100644 index 0000000..c24bbea --- /dev/null +++ b/images/socials/site.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/images/socials/spotify.svg b/images/socials/spotify.svg new file mode 100644 index 0000000..5716102 --- /dev/null +++ b/images/socials/spotify.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/images/socials/twitch.svg b/images/socials/twitch.svg new file mode 100644 index 0000000..6481007 --- /dev/null +++ b/images/socials/twitch.svg @@ -0,0 +1,4 @@ + \ No newline at end of file diff --git a/images/socials/twitter.svg b/images/socials/twitter.svg new file mode 100644 index 0000000..d0a13e5 --- /dev/null +++ b/images/socials/twitter.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/images/socials/youtube.svg b/images/socials/youtube.svg new file mode 100644 index 0000000..e1a882a --- /dev/null +++ b/images/socials/youtube.svg @@ -0,0 +1,3 @@ + \ No newline at end of file diff --git a/images/theme/estrogen.svg b/images/theme/estrogen.svg new file mode 100644 index 0000000..71c5d51 --- /dev/null +++ b/images/theme/estrogen.svg @@ -0,0 +1,55 @@ + + \ No newline at end of file diff --git a/images/theme/frappe.png b/images/theme/frappe.png new file mode 100644 index 0000000..a73f301 Binary files /dev/null and b/images/theme/frappe.png differ diff --git a/images/theme/latte.png b/images/theme/latte.png new file mode 100644 index 0000000..210b9c0 Binary files /dev/null and b/images/theme/latte.png differ diff --git a/images/theme/macchiato.png b/images/theme/macchiato.png new file mode 100644 index 0000000..2a3a74e Binary files /dev/null and b/images/theme/macchiato.png differ diff --git a/images/theme/mocha.png b/images/theme/mocha.png new file mode 100644 index 0000000..f3820c6 Binary files /dev/null and b/images/theme/mocha.png differ diff --git a/js/flavors.js b/js/flavors.js new file mode 100644 index 0000000..3d888ed --- /dev/null +++ b/js/flavors.js @@ -0,0 +1,62 @@ +(function flavors() { + // Metadata only — colors are defined in the per-flavor CSS files. + const FLAVORS = { + mocha: { label: "Mocha", dot: "#f5c2e7" }, + macchiato: { label: "Macchiato", dot: "#f5bde6" }, + frappe: { label: "Frappé", dot: "#f4b8e4" }, + latte: { label: "Latte", dot: "#ea76cb" }, + }; + const ORDER = ["mocha", "macchiato", "frappe", "latte"]; + + const root = document.documentElement; + const ls = window.localStorage; + + function apply(name) { + const f = FLAVORS[name] || FLAVORS.mocha; + root.setAttribute("data-flavor", name); // CSS does the rest + const meta = document.querySelector('meta[name="theme-color"]'); + if (meta) meta.setAttribute("content", f.dot); + } + + let current = ls.getItem("ctpFlavor"); + if (!ORDER.includes(current)) current = "mocha"; + apply(current); // the
snippet already set this to avoid a flash + + // ---- top-right corner icon button ---- + const bar = document.createElement("div"); + bar.className = "beta-bar"; + bar.innerHTML = ` + `; + // Group the single-item widgets (now-playing + theme toggle) into one + // top bar. On mobile they sit side by side; on desktop both stay + // position:fixed, so this wrapper is zero-size and invisible. + let topbar = document.querySelector(".topbar"); + if (!topbar) { + topbar = document.createElement("div"); + topbar.className = "topbar"; + document.body.insertBefore(topbar, document.body.firstChild); + const np = document.getElementById("now-playing"); + if (np) topbar.appendChild(np); + } + topbar.appendChild(bar); + + const btn = bar.querySelector("#flavor-btn"); + const icon = bar.querySelector(".beta-icon"); + + function paintBtn() { + const f = FLAVORS[current]; + icon.src = `/images/theme/${current}.png`; // e.g. /images/theme/mocha.png + icon.alt = f.label; + btn.title = `Theme: ${f.label} (click to cycle)`; + } + paintBtn(); + + btn.addEventListener("click", () => { + current = ORDER[(ORDER.indexOf(current) + 1) % ORDER.length]; + ls.setItem("ctpFlavor", current); + apply(current); + paintBtn(); + }); +})(); diff --git a/js/site-switcher.js b/js/site-switcher.js new file mode 100644 index 0000000..0eb28a8 --- /dev/null +++ b/js/site-switcher.js @@ -0,0 +1,169 @@ +/* Cross-site switcher — a single dropdown that links the sister sites: + * clove.is-a.dev · doughmination.is-a.dev · lumine.is-a.dev + * + * Self-contained: injects its own CSS + markup and slots itself into the + * existing bottom-left page nav (.nav-links). Drop one script tag on any + * page that has the nav and it just works. The current site is detected + * from the hostname and marked, and the redundant single "is-a-dev" pill + * (if present) is removed so we don't double up on cross-site links. + */ +(function siteSwitcher() { + var SITES = [ + { id: "clove", label: "clove.is-a.dev", url: "https://clove.is-a.dev", note: "Link Center" }, + { id: "doughmination", label: "doughmination.is-a.dev", url: "https://doughmination.is-a.dev", note: "Beta Link Center" }, + { id: "lumine", label: "lumine.is-a.dev", url: "https://lumine.is-a.dev", note: "Discord Bots" }, + ]; + + var host = (location.hostname || "").replace(/^www\./, ""); + var currentId = null; + for (var i = 0; i < SITES.length; i++) { + if (host === SITES[i].url.replace("https://", "")) currentId = SITES[i].id; + } + + // ---- styles ------------------------------------------------------------- + var css = ` + .site-switcher { position: relative; } + .site-switcher-btn { + position: relative; + display: inline-flex; + align-items: center; + gap: 0.4rem; + padding: 0.3rem 0.7rem; + border-radius: 999px; + background: var(--surface-1); + border: 1px solid var(--surface-2); + color: var(--text); + font: inherit; + font-size: 0.8rem; + cursor: pointer; + transition: transform 0.15s ease, border-color 0.15s ease, background 0.15s ease; + } + .site-switcher-btn:hover { + border-color: rgb(var(--accent-rgb)); + transform: translateX(2px); + } + .site-switcher-dot { + width: 8px; height: 8px; border-radius: 50%; + background: rgb(var(--accent-rgb)); flex-shrink: 0; + } + .site-switcher-caret { + font-size: 0.7rem; line-height: 1; opacity: 0.8; + transition: transform 0.15s ease; + } + .site-switcher.open .site-switcher-caret { transform: rotate(180deg); } + + .site-switcher-menu { + position: absolute; + left: 0; + bottom: calc(100% + 0.45rem); + display: flex; + flex-direction: column; + gap: 0.25rem; + min-width: 210px; + padding: 0.4rem; + border-radius: 12px; + background: var(--surface-0); + border: 1px solid var(--surface-1); + box-shadow: 0 10px 28px -8px rgba(17, 17, 27, 0.7); + opacity: 0; + transform: translateY(6px); + pointer-events: none; + transition: opacity 0.15s ease, transform 0.15s ease; + z-index: 30; + } + .site-switcher.open .site-switcher-menu { + opacity: 1; + transform: translateY(0); + pointer-events: auto; + } + .site-switcher-item { + display: flex; + flex-direction: column; + gap: 0.05rem; + padding: 0.4rem 0.6rem; + border-radius: 8px; + text-decoration: none; + color: var(--text); + border: 1px solid transparent; + transition: background 0.12s ease, border-color 0.12s ease; + } + .site-switcher-item:hover { background: var(--surface-1); } + .site-switcher-item .ss-label { font-size: 0.82rem; font-weight: 500; } + .site-switcher-item .ss-note { font-size: 0.68rem; color: var(--subtext-0); } + .site-switcher-item.current { + border-color: rgb(var(--accent-rgb)); + background: rgba(var(--accent-rgb), 0.12); + } + .site-switcher-item.current .ss-label { color: rgb(var(--accent-rgb)); } + + @media (max-width: 640px) { + .site-switcher-menu { left: 50%; transform: translate(-50%, 6px); } + .site-switcher.open .site-switcher-menu { transform: translate(-50%, 0); } + }`; + + var style = document.createElement("style"); + style.textContent = css; + document.head.appendChild(style); + + // ---- markup ------------------------------------------------------------- + var current = SITES.filter(function (s) { return s.id === currentId; })[0]; + var btnLabel = current ? current.label : "Sites"; + + var wrap = document.createElement("div"); + wrap.className = "site-switcher"; + + var menuItems = SITES.map(function (s) { + var isCur = s.id === currentId; + return ( + '' + + '' + s.label + "" + + '' + s.note + (isCur ? " · you are here" : "") + "" + + "" + ); + }).join(""); + + wrap.innerHTML = + '" + + '"; + + // ---- mount: into the existing nav, else float bottom-left --------------- + var navLinks = document.querySelector(".nav-links"); + if (navLinks) { + var stale = navLinks.querySelector(".nav-link.is-a-dev"); + if (stale) stale.remove(); + navLinks.appendChild(wrap); + } else { + wrap.style.position = "fixed"; + wrap.style.left = "1rem"; + wrap.style.bottom = "1rem"; + wrap.style.zIndex = "6"; + document.body.appendChild(wrap); + } + + // ---- behaviour ---------------------------------------------------------- + var btn = wrap.querySelector(".site-switcher-btn"); + + function setOpen(open) { + wrap.classList.toggle("open", open); + btn.setAttribute("aria-expanded", open ? "true" : "false"); + } + + btn.addEventListener("click", function (e) { + e.stopPropagation(); + setOpen(!wrap.classList.contains("open")); + }); + + document.addEventListener("click", function (e) { + if (!wrap.contains(e.target)) setOpen(false); + }); + + document.addEventListener("keydown", function (e) { + if (e.key === "Escape") setOpen(false); + }); +})();