25 Commits

Author SHA1 Message Date
58773fc193 release: v3.0.0
Some checks failed
Code Analysis / SonarQube (push) Failing after 17s
Node.js CI / Lint and Test (push) Successful in 40s
2025-07-08 16:46:48 -07:00
5d0660c003 feat!: rename variables to avoid collision (#5)
Some checks failed
Code Analysis / SonarQube (push) Failing after 16s
Node.js CI / Lint and Test (push) Has been cancelled
### Explanation

_No response_

### Issue

_No response_

### Attestations

- [x] I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/)
- [x] I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/).
- [x] My contribution complies with the [Contributor Covenant](https://docs.nhcarrigan.com/dev/covenant/).

### Dependencies

- [x] I have pinned the dependencies to a specific patch version.

### Style

- [x] I have run the linter and resolved any errors.
- [x] My pull request uses an appropriate title, matching the conventional commit standards.
- [x] My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request.

### Tests

- [ ] My contribution adds new code, and I have added tests to cover it.
- [ ] My contribution modifies existing code, and I have updated the tests to reflect these changes.
- [ ] All new and existing tests pass locally with my changes.
- [ ] Code coverage remains at or above the configured threshold.

### Documentation

_No response_

### Versioning

Major - My pull request introduces a breaking change.

Reviewed-on: #5
Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
2025-07-08 16:46:34 -07:00
ab663e9f4b feat: add full social list to footer (#4)
Some checks failed
Code Analysis / SonarQube (push) Failing after 16s
Node.js CI / Lint and Test (push) Successful in 39s
### Explanation

_No response_

### Issue

_No response_

### Attestations

- [x] I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/)
- [x] I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/).
- [x] My contribution complies with the [Contributor Covenant](https://docs.nhcarrigan.com/dev/covenant/).

### Dependencies

- [x] I have pinned the dependencies to a specific patch version.

### Style

- [x] I have run the linter and resolved any errors.
- [x] My pull request uses an appropriate title, matching the conventional commit standards.
- [x] My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request.

### Tests

- [ ] My contribution adds new code, and I have added tests to cover it.
- [ ] My contribution modifies existing code, and I have updated the tests to reflect these changes.
- [ ] All new and existing tests pass locally with my changes.
- [ ] Code coverage remains at or above the configured threshold.

### Documentation

_No response_

### Versioning

_No response_

Reviewed-on: #4
Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
2025-07-08 16:32:40 -07:00
2bd410631b release: v2.1.0
Some checks failed
Code Analysis / SonarQube (push) Failing after 16s
Node.js CI / Lint and Test (push) Successful in 41s
2025-07-04 14:30:54 -07:00
25ec9f8e2a feat: add id to programmatically remove custom styling (#3)
Some checks failed
Code Analysis / SonarQube (push) Failing after 15s
Node.js CI / Lint and Test (push) Has been cancelled
### Explanation

_No response_

### Issue

_No response_

### Attestations

- [x] I have read and agree to the [Code of Conduct](https://docs.nhcarrigan.com/community/coc/)
- [x] I have read and agree to the [Community Guidelines](https://docs.nhcarrigan.com/community/guide/).
- [x] My contribution complies with the [Contributor Covenant](https://docs.nhcarrigan.com/dev/covenant/).

### Dependencies

- [ ] I have pinned the dependencies to a specific patch version.

### Style

- [x] I have run the linter and resolved any errors.
- [x] My pull request uses an appropriate title, matching the conventional commit standards.
- [x] My scope of feat/fix/chore/etc. correctly matches the nature of changes in my pull request.

### Tests

- [ ] My contribution adds new code, and I have added tests to cover it.
- [ ] My contribution modifies existing code, and I have updated the tests to reflect these changes.
- [ ] All new and existing tests pass locally with my changes.
- [ ] Code coverage remains at or above the configured threshold.

### Documentation

_No response_

### Versioning

_No response_

Reviewed-on: #3
Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Co-committed-by: Naomi Carrigan <commits@nhcarrigan.com>
2025-07-04 14:30:25 -07:00
1f450156c8 2.0.0
Some checks failed
Code Analysis / SonarQube (push) Failing after 18s
Node.js CI / Lint and Test (push) Successful in 43s
2025-06-27 14:22:09 -07:00
5e9d0dd11e feat: copyright should link to homepage 2025-06-27 14:21:56 -07:00
4f05dbafbb feat: remove border radius from CTA avatar
Some checks failed
Code Analysis / SonarQube (push) Failing after 16s
Node.js CI / Lint and Test (push) Successful in 43s
2025-06-12 15:23:04 -07:00
934de19c1e release: v1.8.0
Some checks failed
Node.js CI / Lint and Test (push) Successful in 1m8s
Code Analysis / SonarQube (push) Failing after 1m21s
2025-05-31 17:39:55 -07:00
4a76b22119 feat: replace audio button with donate badge
Some checks failed
Code Analysis / SonarQube (push) Has been cancelled
Node.js CI / Lint and Test (push) Has been cancelled
2025-05-31 17:39:34 -07:00
542aef157a release: v1.7.0
Some checks failed
Node.js CI / Lint and Test (push) Successful in 1m18s
Code Analysis / SonarQube (push) Failing after 1m26s
2025-05-30 21:05:18 -07:00
2e72c34c2d feat: include google ads snippet 2025-05-30 21:04:38 -07:00
c9ae8804eb feat: remove video overlay
Some checks failed
Node.js CI / Lint and Test (push) Successful in 1m13s
Code Analysis / SonarQube (push) Failing after 1m24s
2025-05-08 16:55:33 -07:00
a5f17bb2eb feat: use logo in modalx
Some checks failed
Node.js CI / Lint and Test (push) Successful in 1m18s
Code Analysis / SonarQube (push) Failing after 1m28s
2025-05-08 16:54:00 -07:00
06e58752b9 release: v1.6.0
Some checks failed
Node.js CI / Lint and Test (push) Successful in 1m13s
Code Analysis / SonarQube (push) Failing after 1m22s
2025-04-04 15:06:21 -07:00
db4dcc3090 feat: track urls and page titles 2025-04-04 15:06:04 -07:00
53aa95c9c2 fix: tweak the colours a bit
Some checks failed
Node.js CI / Lint and Test (push) Successful in 1m12s
Code Analysis / SonarQube (push) Failing after 1m22s
2025-03-31 15:42:25 -07:00
93a2339c92 release: v1.5.0
Some checks failed
Node.js CI / Lint and Test (push) Successful in 1m14s
Code Analysis / SonarQube (push) Failing after 1m25s
2025-03-27 14:38:28 -07:00
3e3213c554 feat: add dark mode and theme selector
Some checks failed
Code Analysis / SonarQube (push) Has been cancelled
Node.js CI / Lint and Test (push) Has been cancelled
2025-03-27 14:38:10 -07:00
2e7d5d26e4 fix: lkint
All checks were successful
Node.js CI / Lint and Test (push) Successful in 1m9s
Code Analysis / SonarQube (push) Successful in 1m17s
2025-03-11 12:08:14 -07:00
52a7b5b812 release: v1.4.0
Some checks failed
Node.js CI / Lint and Test (push) Failing after 48s
Code Analysis / SonarQube (push) Has been cancelled
2025-03-11 12:07:15 -07:00
6be8026024 feat: add blinkies
Some checks failed
Node.js CI / Lint and Test (push) Has been cancelled
Code Analysis / SonarQube (push) Has been cancelled
2025-03-11 12:06:59 -07:00
c10089c351 release: v1.3.0
All checks were successful
Node.js CI / Lint and Test (push) Successful in 58s
Code Analysis / SonarQube (push) Successful in 1m6s
2025-03-01 00:23:11 -08:00
5b8b597d7d feat: change to pink theme
All checks were successful
Node.js CI / Lint and Test (push) Successful in 59s
Code Analysis / SonarQube (push) Successful in 1m8s
2025-03-01 00:19:48 -08:00
dd7d4042ea chore: sonar workflow
All checks were successful
Node.js CI / Lint and Test (push) Successful in 59s
Code Analysis / SonarQube (push) Successful in 1m10s
2025-02-26 13:26:47 -08:00
5 changed files with 466 additions and 198 deletions

View File

@ -0,0 +1,34 @@
name: Code Analysis
on:
push:
branches:
- main
jobs:
sonar:
name: SonarQube
steps:
- name: Checkout Source Files
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: SonarCube Scan
uses: SonarSource/sonarqube-scan-action@v4
timeout-minutes: 10
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: "https://quality.nhcarrigan.com"
with:
args: >
-Dsonar.sources=.
-Dsonar.projectKey=website-headers
- name: SonarQube Quality Gate check
uses: sonarsource/sonarqube-quality-gate-action@v1
with:
pollingTimeoutSec: 600
env:
SONAR_TOKEN: ${{ secrets.SONAR_TOKEN }}
SONAR_HOST_URL: "https://quality.nhcarrigan.com"

View File

@ -8,5 +8,10 @@
"sonarlint.connectedMode.project": {
"connectionId": "nhcarrigan",
"projectKey": "nhcarrigan_website-headers"
}
},
"cSpell.words": [
"Adsense",
"nhcarrigan",
"noreferrer"
]
}

View File

@ -5,6 +5,25 @@
<main>
<h1>Test</h1>
<p>Just a demo page.</p>
<div>
Pokem ipsum dolor sit amet Red Natu Misdreavus Marowak Kyogre Deoxys. Glitch City Rampardos Giovanni Heatran Turtwig Scratch Lileep. Sunt in culpa Regirock Deerling Togekiss Shaymin Serperior Reshiram. Normal Gurdurr Probopass Ampharos The Power Of One Vigoroth Pichu. Mirror Move Delibird Slowking Nurse Joy Cherrim Vanillish Hippowdon.
Gold Maractus Feebas Thunder Badge blast off at the speed of light Mareep Ice. Rock Ditto Pachirisu Glaceon Vulpix Teleport Qwilfish. Fuchsia City Pidgeot Leaf Green Stantler Electric Hidden Machine Goldeen. Bulbasaur Swadloon Sandslash Beldum Serperior Burmy Deoxys. Charmander Pokemon The Movie 2000 excepteur sint occaecat cupidatat non proident Pokemon The Movie 2000 Cubchoo Magnemite Pansage.
Lorem ipsum dolor sit amet S.S. Anne Haxorus Lapras Dark Baltoy Kingler. Fog Badge Jolteon Accelgor Purugly Chikorita Probopass Glalie. Poison Sting Braviary Alakazam Darkrai Kricketune Toxicroak Cofagrigus. Razor Leaf Mew Pokemon 4Ever a wild Pokemon appeared Staravia Timburr Beheeyem. Teleport Dusclops Timburr Houndour Nidorino Haxorus Pignite.
Thundershock Porygon lorem ipsum dolor sit amet Roserade Wobbuffet Moltres Venonat. Splash Parasect Piplup Rotom Razor Leaf Rotom Persian. Viridian City Ghost Tail Whip Smoochum Gurdurr Giratina Tyranitar. Misty Slowking Cubchoo Ciccino Flaaffy Crustle Dratini. Celadon Department Store Purugly Clamperl Kricketot Lanturn Wurmple Cyndaquil.
Glacier Badge Magikarp used Splash Nidoran Smoochum Bidoof Regirock Mewtwo. Sed do eiusmod tempor incididunt Volcarona Yanmega Probopass Weepinbell Marowak Hitmonchan. Charmeleon Johto Garbodor Octillery Soda Pop Liepard Mantine. Plain Badge Watchog Baltoy Zephyr Badge Cleffa Pokemon The Movie 2000 Whiscash. Mewtwo Strikes Back Ice Virizion prepare for trouble Azumarill Darumaka Snorlax.
Electric Golurk Alakazam lorem ipsum dolor sit amet Plain Badge Lotad Poliwhirl. Dragon Rage Kabuto Rotom Ponyta Golurk Vigoroth Seadra. Mewtwo Strikes Back Braviary Patrat Poison Kakuna Tropius Technical Machine. Thundershock Jigglypuff Pikachu Ampharos Ambipom Ditto Elgyem. Fire Red Lillipup Breloom Cranidos Squirtle Skiploom I wanna be the very best.
Vermilion City Minccino Prinplup Gothitelle Thundurus searching far and wide Golbat. Blue Magcargo Honchkrow Delcatty Mawile Gigalith Nidorina. Fuchsia City Youngster wants to fight Patrat Pidgeot Conkeldurr Pokemon Fan Club Chairman Magneton. Teleport Ditto Woobat Mandibuzz Eelektross Burnt Berry Rhydon. Dark Ash Mr. Mime Musharna Mew Bidoof Volcano Badge.
Brock Ash's mother Kricketot Hariyama Metagross Crawdaunt Marshtomp. Charizard Breloom Maractus Servine Burnt Berry Gothita Treecko. Leaf Green Squirtle Drilbur Tynamo Remoraid Trapinch Scolipede. Team Rocket Leech Seed Torchic Foongus Mirror Move Grovyle Clefable. Velit esse cillum dolore eu fugiat nulla pariatur Cloyster Feebas Carvanha Leech Life Pidgeot Venomoth.
Yellow Purrloin Nurse Joy Voltorb Mienfoo Voltorb Tail Whip. Sed do eiusmod tempor incididunt Bidoof Oddish Spheal Jirachi Garchomp Liepard. Storm Badge Shelgon Musharna Flareon Terrakion Shedinja Marowak. Charmeleon Bonsly Wigglytuff Regice Mantyke Ash Ketchum Marsh Badge. Silver Forretress Tynamo Masquerain Tangrowth Igglybuff Clamperl.
</div>
</main>
</body>
<script src="./prod/index.js"></script>

View File

@ -1,6 +1,6 @@
{
"name": "website-headers",
"version": "1.2.1",
"version": "3.0.0",
"description": "",
"main": "index.js",
"type": "module",

View File

@ -5,10 +5,10 @@
*/
// #region Version
const version = "{{ version }}";
const nhcarriganHeadersVersion = "{{ version }}";
console.log(`
========================================
Loading NHCarrigan library v${version}.
Loading NHCarrigan library v${nhcarriganHeadersVersion}.
Copyright (c) ${new Date().getFullYear().
toString()} NHCarrigan
Changelog: https://codeberg.org/nhcarrigan/website-headers/releases
@ -19,12 +19,18 @@ Questions? Contact us at https://docs.nhcarrigan.com/about/contact
// #endregion
// #region Query Selectors
const head = document.querySelector("head");
const body = document.querySelector("body");
const title = document.querySelector("title");
const description = document.querySelector(`meta[name="description"]`);
const nhcarriganHeadersHead = document.querySelector("head");
const nhcarriganHeadersBody = document.querySelector("body");
const nhcarriganHeadersTitle = document.querySelector("title");
const nhcarriganHeadersDescription = document.querySelector(
`meta[name="description"]`,
);
const { href: url, hostname } = window.location;
const {
href: nhcarriganHeadersUrl,
hostname: nhcarriganHeadersHostname,
pathname: nhcarriganHeadersPathname,
} = window.location;
// #endregion
@ -34,86 +40,104 @@ const { href: url, hostname } = window.location;
* The title and description are set by each website. This should
* only load things like open graph data and favicons.
*/
const openGraphTitle = document.createElement("meta");
openGraphTitle.setAttribute("property", "og:title");
openGraphTitle.setAttribute("content", title?.innerText ?? "NHCarrigan");
const openGraphDescription = document.createElement("meta");
openGraphDescription.setAttribute("property", "og:description");
openGraphDescription.setAttribute(
const nhcarriganHeadersOpenGraphTitle = document.createElement("meta");
nhcarriganHeadersOpenGraphTitle.setAttribute("property", "og:title");
nhcarriganHeadersOpenGraphTitle.setAttribute(
"content",
description?.getAttribute("content")
// eslint-disable-next-line stylistic/max-len
nhcarriganHeadersTitle?.innerText ?? "NHCarrigan",
);
const nhcarriganHeadersOpenGraphDescription = document.createElement("meta");
nhcarriganHeadersOpenGraphDescription.setAttribute(
"property",
"og:description",
);
nhcarriganHeadersOpenGraphDescription.setAttribute(
"content",
nhcarriganHeadersDescription?.getAttribute("content")
?? "We are a software engineering and community management consulting firm.",
);
const openGraphImage = document.createElement("meta");
openGraphImage.setAttribute("property", "og:image");
openGraphImage.setAttribute(
const nhcarriganHeadersOpenGraphImage = document.createElement("meta");
nhcarriganHeadersOpenGraphImage.setAttribute("property", "og:image");
nhcarriganHeadersOpenGraphImage.setAttribute(
"content",
"https://cdn.nhcarrigan.com/og-image.png",
);
const openGraphUrl = document.createElement("meta");
openGraphUrl.setAttribute("property", "og:url");
openGraphUrl.setAttribute("content", url);
const openGraphType = document.createElement("meta");
openGraphType.setAttribute("property", "og:type");
openGraphType.setAttribute("content", "website");
const nhcarriganHeadersOpenGraphUrl = document.createElement("meta");
nhcarriganHeadersOpenGraphUrl.setAttribute("property", "og:url");
nhcarriganHeadersOpenGraphUrl.setAttribute("content", nhcarriganHeadersUrl);
const nhcarriganHeadersOpenGraphType = document.createElement("meta");
nhcarriganHeadersOpenGraphType.setAttribute("property", "og:type");
nhcarriganHeadersOpenGraphType.setAttribute("content", "website");
const twitterCard = document.createElement("meta");
twitterCard.setAttribute("name", "twitter:card");
twitterCard.setAttribute("content", "summary_large_image");
const twitterDomain = document.createElement("meta");
twitterDomain.setAttribute("name", "twitter:domain");
twitterDomain.setAttribute("content", hostname);
const twitterUrl = document.createElement("meta");
twitterUrl.setAttribute("name", "twitter:url");
twitterUrl.setAttribute("content", url);
const twitterTitle = document.createElement("meta");
twitterTitle.setAttribute("name", "twitter:title");
twitterTitle.setAttribute("content", title?.innerText ?? "NHCarrigan");
const twitterDescription = document.createElement("meta");
twitterDescription.setAttribute("name", "twitter:description");
twitterDescription.setAttribute(
const nhcarriganHeadersTwitterCard = document.createElement("meta");
nhcarriganHeadersTwitterCard.setAttribute("name", "twitter:card");
nhcarriganHeadersTwitterCard.setAttribute("content", "summary_large_image");
const nhcarriganHeadersTwitterDomain = document.createElement("meta");
nhcarriganHeadersTwitterDomain.setAttribute("name", "twitter:domain");
nhcarriganHeadersTwitterDomain.setAttribute(
"content",
description?.getAttribute("content")
// eslint-disable-next-line stylistic/max-len
nhcarriganHeadersHostname,
);
const nhcarriganHeadersTwitterUrl = document.createElement("meta");
nhcarriganHeadersTwitterUrl.setAttribute("name", "twitter:url");
nhcarriganHeadersTwitterUrl.setAttribute("content", nhcarriganHeadersUrl);
const nhcarriganHeadersTwitterTitle = document.createElement("meta");
nhcarriganHeadersTwitterTitle.setAttribute("name", "twitter:title");
nhcarriganHeadersTwitterTitle.setAttribute(
"content",
nhcarriganHeadersTitle?.innerText ?? "NHCarrigan",
);
const nhcarriganHeadersTwitterDescription = document.createElement("meta");
nhcarriganHeadersTwitterDescription.setAttribute("name", "twitter:description");
nhcarriganHeadersTwitterDescription.setAttribute(
"content",
nhcarriganHeadersDescription?.getAttribute("content")
?? "We are a software engineering and community management consulting firm.",
);
const twitterImage = document.createElement("meta");
twitterImage.setAttribute("name", "twitter:image");
twitterImage.setAttribute("content", "https://cdn.nhcarrigan.com/og-image.png");
const nhcarriganHeadersTwitterImage = document.createElement("meta");
nhcarriganHeadersTwitterImage.setAttribute("name", "twitter:image");
nhcarriganHeadersTwitterImage.setAttribute(
"content",
"https://cdn.nhcarrigan.com/og-image.png",
);
// #endregion
// #region Favicon
const favicon = document.createElement("link");
favicon.rel = "icon";
favicon.type = "image/x-icon";
favicon.href = "https://cdn.nhcarrigan.com/favicon/favicon.ico";
const appleTouchIcon = document.createElement("link");
appleTouchIcon.rel = "apple-touch-icon";
appleTouchIcon.href = "https://cdn.nhcarrigan.com/favicon/apple-touch-icon.png";
const smallIcon = document.createElement("link");
smallIcon.rel = "icon";
smallIcon.href = "https://cdn.nhcarrigan.com/favicon/favicon-16x16.png";
const largeIcon = document.createElement("link");
largeIcon.rel = "icon";
largeIcon.href = "https://cdn.nhcarrigan.com/favicon/favicon-32x32.png";
const nhcarriganHeadersFavicon = document.createElement("link");
nhcarriganHeadersFavicon.rel = "icon";
nhcarriganHeadersFavicon.type = "image/x-icon";
nhcarriganHeadersFavicon.href
= "https://cdn.nhcarrigan.com/favicon/favicon.ico";
const nhcarriganHeadersAppleTouchIcon = document.createElement("link");
nhcarriganHeadersAppleTouchIcon.rel = "apple-touch-icon";
nhcarriganHeadersAppleTouchIcon.href
= "https://cdn.nhcarrigan.com/favicon/apple-touch-icon.png";
const nhcarriganHeadersSmallIcon = document.createElement("link");
nhcarriganHeadersSmallIcon.rel = "icon";
nhcarriganHeadersSmallIcon.href
= "https://cdn.nhcarrigan.com/favicon/favicon-16x16.png";
const nhcarriganHeadersLargeIcon = document.createElement("link");
nhcarriganHeadersLargeIcon.rel = "icon";
nhcarriganHeadersLargeIcon.href
= "https://cdn.nhcarrigan.com/favicon/favicon-32x32.png";
// #endregion
// #region Styles
const styles = document.createElement("style");
styles.innerHTML = `
const nhcarriganHeadersStyles = document.createElement("style");
nhcarriganHeadersStyles.id = "nhcarrigan-global-styles";
nhcarriganHeadersStyles.innerHTML = `
@font-face {
font-family: 'OpenDyslexic';
src: url('https://cdn.nhcarrigan.com/fonts/OpenDyslexicMono-Regular.otf') format('opentype');
}
:root {
--foreground: #04624f;
--background: #abfcecdd;
--foreground: #2a0a18;
--background: #ffb6c1bb;
}
* {
@ -152,14 +176,11 @@ main {
width: 95%;
max-width: 1080px;
margin: auto;
margin-bottom: 100px;
margin-bottom: 85px;
padding: 10px;
}
footer {
width: 100%;
display: flex;
justify-content: space-between;
align-items: center;
color: var(--foreground);
background-color: var(--background);
position: fixed;
@ -167,6 +188,29 @@ footer {
height: 75px;
padding: 0 10px;
}
#footer-inner-container {
display: flex;
align-items: center;
justify-content: space-between;
height: 75px;
}
#footer-badge-container {
display: grid;
grid-template-columns: repeat(8, 1fr);
align-items: center;
justify-content: space-around;
}
#show-socials-button, #theme-select-button {
background: none;
border: none;
cursor: url('https://cdn.nhcarrigan.com/cursors/pointer.cur'), pointer;
color: var(--foreground);
font-size: 1rem;
font-family: 'OpenDyslexic', monospace;
}
#show-socials-button > i, #theme-select-button > i {
font-size: 1.5rem;
}
a {
color: unset;
cursor: url('https://cdn.nhcarrigan.com/cursors/pointer.cur'), pointer;
@ -175,16 +219,52 @@ a {
display: flex;
align-items: center;
}
#audio-theme-button {
background: none;
border: none;
cursor: url('https://cdn.nhcarrigan.com/cursors/pointer.cur'), pointer;
#social-list {
position: absolute;
bottom: 75px;
left: 0;
right: 0;
width: 100vw;
max-width: 400px;
padding: 10px;
background-color: var(--background);
color: var(--foreground);
border-radius: 10px;
border: 1px solid var(--foreground);
display: none;
z-index: 1000;
}
@media screen and (max-width: 600px) {
.social-list-item {
padding: 10px;
}
.social-list-item > a {
display: flex;
align-items: center;
justify-content: space-between;
text-decoration: none;
}
.social-list-divider {
border: 0.5px solid var(--foreground);
}
.social-list-item:hover {
background-color: var(--foreground);
color: var(--background);
}
.is-dark {
--foreground: #ffb6c1;
--background: #2a0a18bb;
}
@media screen and (max-width: 625px) {
#tree-nation-offset-website {
display: none;
}
footer, #footer-inner-container {
height: 50px;
justify-content: space-around;
}
main {
margin-bottom: 60px;
}
}
`;
@ -192,45 +272,86 @@ a {
// #region Components
const footer = document.createElement("footer");
footer.innerHTML = `
<p>&copy; Naomi Carrigan</p>
<a href="https://chat.nhcarrigan.com" target="_blank" rel="noreferrer">
<i class="fa-solid fa-comments"></i>
</a>
<button id="audio-theme-button" type="button">
<i class="fa-solid fa-play"></i>
const nhcarriganHeadersFooter = document.createElement("footer");
nhcarriganHeadersFooter.innerHTML = `
<div id="footer-inner-container">
<p>&copy; <a href="https://nhcarrigan.com" target="_blank">Naomi Carrigan</a></p>
<button id="show-socials-button" type="button">
<i class="fa-solid fa-share-nodes"></i> Connect with Us
</button>
<button id="theme-select-button" type="button">
<i id="theme-select-icon" class="fa-solid fa-moon"></i> Toggle Theme
</button>
<a href="https://buy.stripe.com/cN24iTfqu1j6b3afZ2" target="_blank" rel="noreferrer">
<img src="https://cdn.nhcarrigan.com/donate.png" alt="Donate" style="width: 70px; height: 70px;">
</a>
<div id="tree-nation-offset-website"></div>
</div>
<div id="social-list">
<div class="social-list-item">
<a href="https://chat.nhcarrigan.com" target="_blank" rel="noreferrer">
<i class="fa-brands fa-discord"></i><span>Join our Discord~!</span>
</a>
</div>
<hr class="social-list-divider" />
<div class="social-list-item">
<a href="https://forum.nhcarrigan.com" target="_blank" rel="noreferrer">
<i class="fa-brands fa-discourse"></i><span>Sign up for our forum~!</span>
</a>
</div>
<hr class="social-list-divider" />
<div class="social-list-item">
<a href="https://git.nhcarrigan.com" target="_blank" rel="noreferrer">
<i class="fa-brands fa-git-alt"></i><span>Check out our source code~!</span>
</a>
</div>
<hr class="social-list-divider" />
<div class="social-list-item">
<a href="https://bsky.app/profile/nhcarrigan.com" target="_blank" rel="noreferrer">
<i class="fa-brands fa-bluesky"></i><span>Follow us on Bluesky~!</span>
</a>
</div>
<hr class="social-list-divider" />
<div class="social-list-item">
<a href="https://www.linkedin.com/company/nhcarrigan" target="_blank" rel="noreferrer">
<i class="fa-brands fa-linkedin"></i><span>Connect with us on LinkedIn~!</span>
</a>
</div>
<hr class="social-list-divider" />
<div class="social-list-item">
<a href="https://www.reddit.com/r/nhcarrigan/" target="_blank" rel="noreferrer">
<i class="fa-brands fa-reddit"></i><span>Join our subreddit~!</span>
</a>
</div>
<hr class="social-list-divider" />
<div class="social-list-item">
<a href="https://www.youtube.com/@naomilgbt" target="_blank" rel="noreferrer">
<i class="fa-brands fa-youtube"></i><span>Subscribe to our YouTube~!</span>
</a>
</div>
<hr class="social-list-divider" />
<div class="social-list-item">
<a href="https://twitch.tv/naomilgbt" target="_blank" rel="noreferrer">
<i class="fa-brands fa-twitch"></i><span>Subscribe to our Twitch~!</span>
</a>
</div>
<hr class="social-list-divider" />
<div class="social-list-item">
<a href="https://x.com/nhcarrigan1" target="_blank" rel="noreferrer">
<i class="fa-brands fa-twitter"></i><span>We are even on Twitter~!</span>
</a>
</div>
</div>
`;
const videoOverlay = document.createElement("video");
videoOverlay.autoplay = true;
videoOverlay.loop = true;
videoOverlay.muted = true;
videoOverlay.playsInline = true;
videoOverlay.src = "https://cdn.nhcarrigan.com/overlay.webm";
videoOverlay.style.pointerEvents = "none";
videoOverlay.style.position = "fixed";
videoOverlay.style.top = "0";
videoOverlay.style.left = "0";
videoOverlay.style.opacity = "0.25";
videoOverlay.style.width = "100vw";
videoOverlay.style.height = "100vh";
videoOverlay.style.objectFit = "cover";
videoOverlay.style.zIndex = "10000";
// #endregion
// #region Scripts
const treeNation = document.createElement("script");
treeNation.src
const nhcarriganHeadersTreeNation = document.createElement("script");
nhcarriganHeadersTreeNation.src
= "https://widgets.tree-nation.com/js/widgets/v1/widgets.min.js?v=1.0";
const treeNationBottom = document.createElement("script");
treeNationBottom.defer = true;
treeNationBottom.async = true;
treeNationBottom.innerHTML = `
const nhcarriganHeadersTreeNationBottom = document.createElement("script");
nhcarriganHeadersTreeNationBottom.defer = true;
nhcarriganHeadersTreeNationBottom.async = true;
nhcarriganHeadersTreeNationBottom.innerHTML = `
const interval = setInterval(() => {
const tree = document.querySelector("#tree-nation-offset-website");
if (!tree) {
@ -245,93 +366,167 @@ const interval = setInterval(() => {
clearInterval(interval);
}, 1000);
`;
const fontAwesome = document.createElement("script");
fontAwesome.src = "https://kit.fontawesome.com/f949111719.js";
const analytics = document.createElement("script");
analytics.defer = true;
analytics.setAttribute("domain", "nhcarrigan.com");
analytics.src
const nhcarriganHeadersFontAwesome = document.createElement("script");
nhcarriganHeadersFontAwesome.src = "https://kit.fontawesome.com/f949111719.js";
const nhcarriganHeadersAnalytics = document.createElement("script");
nhcarriganHeadersAnalytics.defer = true;
nhcarriganHeadersAnalytics.setAttribute("domain", "nhcarrigan.com");
nhcarriganHeadersAnalytics.src
// eslint-disable-next-line stylistic/max-len
= "https://analytics.nhcarrigan.com/js/script.file-downloads.hash.outbound-links.pageview-props.revenue.tagged-events.js";
const analytics2 = document.createElement("script");
analytics2.innerHTML = `
nhcarriganHeadersAnalytics.setAttribute(
"event-domain",
nhcarriganHeadersHostname,
);
nhcarriganHeadersAnalytics.setAttribute("data-domain", "nhcarrigan.com");
nhcarriganHeadersAnalytics.setAttribute(
"event-page",
nhcarriganHeadersTitle?.innerText ?? "Unknown Page",
);
nhcarriganHeadersAnalytics.setAttribute(
"event-path",
nhcarriganHeadersPathname,
);
const nhcarriganHeadersAnalytics2 = document.createElement("script");
nhcarriganHeadersAnalytics2.innerHTML = `
window.plausible = window.plausible ??
function() {
(window.plausible.q = window.plausible.q ?? []).push(arguments)
}
`;
const nhcarriganHeadersGoogleAdsense = document.createElement("script");
nhcarriganHeadersGoogleAdsense.async = true;
nhcarriganHeadersGoogleAdsense.src
// eslint-disable-next-line stylistic/max-len -- big boi string
= "https://pagead2.googlesyndication.com/pagead/js/adsbygoogle.js?client=ca-pub-3569924701890974";
nhcarriganHeadersGoogleAdsense.setAttribute("crossorigin", "anonymous");
// #endregion
// #region Inject Elements
head?.appendChild(openGraphTitle);
head?.appendChild(openGraphDescription);
head?.appendChild(openGraphImage);
head?.appendChild(openGraphUrl);
head?.appendChild(openGraphType);
head?.appendChild(twitterCard);
head?.appendChild(twitterDomain);
head?.appendChild(twitterUrl);
head?.appendChild(twitterTitle);
head?.appendChild(twitterDescription);
head?.appendChild(twitterImage);
nhcarriganHeadersHead?.appendChild(nhcarriganHeadersOpenGraphTitle);
nhcarriganHeadersHead?.appendChild(nhcarriganHeadersOpenGraphDescription);
nhcarriganHeadersHead?.appendChild(nhcarriganHeadersOpenGraphImage);
nhcarriganHeadersHead?.appendChild(nhcarriganHeadersOpenGraphUrl);
nhcarriganHeadersHead?.appendChild(nhcarriganHeadersOpenGraphType);
nhcarriganHeadersHead?.appendChild(nhcarriganHeadersTwitterCard);
nhcarriganHeadersHead?.appendChild(nhcarriganHeadersTwitterDomain);
nhcarriganHeadersHead?.appendChild(nhcarriganHeadersTwitterUrl);
nhcarriganHeadersHead?.appendChild(nhcarriganHeadersTwitterTitle);
nhcarriganHeadersHead?.appendChild(nhcarriganHeadersTwitterDescription);
nhcarriganHeadersHead?.appendChild(nhcarriganHeadersTwitterImage);
head?.appendChild(favicon);
head?.appendChild(appleTouchIcon);
head?.appendChild(smallIcon);
head?.appendChild(largeIcon);
nhcarriganHeadersHead?.appendChild(nhcarriganHeadersFavicon);
nhcarriganHeadersHead?.appendChild(nhcarriganHeadersAppleTouchIcon);
nhcarriganHeadersHead?.appendChild(nhcarriganHeadersSmallIcon);
nhcarriganHeadersHead?.appendChild(nhcarriganHeadersLargeIcon);
head?.appendChild(styles);
nhcarriganHeadersHead?.appendChild(nhcarriganHeadersStyles);
head?.appendChild(treeNation);
head?.appendChild(fontAwesome);
head?.appendChild(analytics);
head?.appendChild(analytics2);
nhcarriganHeadersHead?.appendChild(nhcarriganHeadersTreeNation);
nhcarriganHeadersHead?.appendChild(nhcarriganHeadersFontAwesome);
nhcarriganHeadersHead?.appendChild(nhcarriganHeadersAnalytics);
nhcarriganHeadersHead?.appendChild(nhcarriganHeadersAnalytics2);
nhcarriganHeadersHead?.appendChild(nhcarriganHeadersGoogleAdsense);
body?.appendChild(footer);
body?.appendChild(videoOverlay);
body?.appendChild(treeNationBottom);
nhcarriganHeadersBody?.appendChild(nhcarriganHeadersFooter);
nhcarriganHeadersBody?.appendChild(nhcarriganHeadersTreeNationBottom);
// #endregion
// #region Audio
// #region Theme
const playButton = document.querySelector("#audio-theme-button");
const audio = new Audio("https://cdn.nhcarrigan.com/theme.mp3");
let playing = false;
playButton?.addEventListener("click", () => {
if (playing) {
audio.pause();
playing = false;
playButton.innerHTML = "<i class=\"fa-solid fa-play\"></i>";
} else {
void audio.play();
playing = true;
playButton.innerHTML = "<i class=\"fa-solid fa-pause\"></i>";
const nhcarriganHeadersThemeButton = document.querySelector(
"#theme-select-button",
);
const nhcarriganHeadersThemeIcon = document.querySelector("#theme-select-icon");
if (localStorage.getItem("theme") === "dark") {
nhcarriganHeadersThemeIcon?.classList.remove("fa-moon");
nhcarriganHeadersThemeIcon?.classList.add("fa-sun");
document.querySelector("html")?.classList.add("is-dark");
}
const nhcarriganHeadersToggleTheme = (): void => {
const nhcarriganHeadersCurrentTheme = localStorage.getItem("theme");
if (nhcarriganHeadersCurrentTheme === "dark") {
localStorage.setItem("theme", "light");
nhcarriganHeadersThemeIcon?.classList.remove("fa-sun");
nhcarriganHeadersThemeIcon?.classList.add("fa-moon");
document.querySelector("html")?.classList.remove("is-dark");
return;
}
localStorage.setItem("theme", "dark");
nhcarriganHeadersThemeIcon?.classList.remove("fa-moon");
nhcarriganHeadersThemeIcon?.classList.add("fa-sun");
document.querySelector("html")?.classList.add("is-dark");
};
nhcarriganHeadersThemeButton?.addEventListener(
"click",
nhcarriganHeadersToggleTheme,
);
const nhcarriganHeadersPrefersDark = window.matchMedia(
"(prefers-color-scheme: dark)",
);
if (
nhcarriganHeadersPrefersDark.matches
&& localStorage.getItem("theme") !== null
) {
localStorage.setItem("theme", "dark");
nhcarriganHeadersThemeIcon?.classList.remove("fa-moon");
nhcarriganHeadersThemeIcon?.classList.add("fa-sun");
document.querySelector("html")?.classList.add("is-dark");
}
});
// #endregion
// #region Social Toggle
const nhcarriganHeadersShowSocialsButton
= document.querySelector<HTMLButtonElement>("#show-socials-button");
const nhcarriganHeadersSocialList
= document.querySelector<HTMLDivElement>("#social-list");
const nhcarriganHeadersToggleSocials = (): void => {
if (!nhcarriganHeadersSocialList) {
throw new Error("Social list element not found.");
}
if (nhcarriganHeadersSocialList.style.display === "block") {
nhcarriganHeadersSocialList.style.display = "none";
nhcarriganHeadersShowSocialsButton?.setAttribute("aria-expanded", "false");
nhcarriganHeadersShowSocialsButton?.setAttribute(
"aria-label",
"Show Socials",
);
return;
}
nhcarriganHeadersSocialList.style.display = "block";
nhcarriganHeadersShowSocialsButton?.setAttribute("aria-expanded", "true");
nhcarriganHeadersShowSocialsButton?.setAttribute(
"aria-label",
"Hide Socials",
);
};
nhcarriganHeadersShowSocialsButton?.addEventListener(
"click",
nhcarriganHeadersToggleSocials,
);
// #region CTA
const cta = document.createElement("dialog");
cta.style.position = "fixed";
cta.style.top = "50%";
cta.style.left = "50%";
cta.style.transform = "translate(-50%, -50%)";
cta.style.padding = "10px";
cta.style.borderRadius = "10px";
cta.style.backgroundColor = "var(--background)";
cta.style.color = "var(--foreground)";
cta.style.textAlign = "center";
cta.style.width = "95%";
cta.style.maxWidth = "400px";
cta.id = "community-cta";
cta.innerHTML = `
const nhcarriganHeadersCta = document.createElement("dialog");
nhcarriganHeadersCta.style.position = "fixed";
nhcarriganHeadersCta.style.top = "50%";
nhcarriganHeadersCta.style.left = "50%";
nhcarriganHeadersCta.style.transform = "translate(-50%, -50%)";
nhcarriganHeadersCta.style.padding = "10px";
nhcarriganHeadersCta.style.borderRadius = "10px";
nhcarriganHeadersCta.style.backgroundColor = "var(--background)";
nhcarriganHeadersCta.style.color = "var(--foreground)";
nhcarriganHeadersCta.style.textAlign = "center";
nhcarriganHeadersCta.style.width = "95%";
nhcarriganHeadersCta.style.maxWidth = "400px";
nhcarriganHeadersCta.id = "community-cta";
nhcarriganHeadersCta.innerHTML = `
<h1 autofocus>Hello~!</h1>
<div style="display: flex; justify-content: space-around; margin-bottom: 10px;">
<img src="https://cdn.nhcarrigan.com/profile.png" alt="Naomi Carrigan" style="width: 100px; height: 100px; border-radius: 50%;">
<img src="https://cdn.nhcarrigan.com/logo.png" alt="NHCarrigan Logo" style="width: 100px; height: 100px;">
<p>
Consider joining our community so you can keep up to date on all of our latest activities!
</p>
@ -339,47 +534,62 @@ cta.innerHTML = `
<a href="https://chat.nhcarrigan.com" target="_blank" rel="noreferrer" style="padding: 10px; background: var(--foreground); color: var(--background); outline: none">Okay, take me there~!</a>
`;
const modalBg = document.createElement("div");
modalBg.style.zIndex = "4999";
modalBg.style.position = "fixed";
modalBg.style.top = "0";
modalBg.style.left = "0";
modalBg.style.width = "100vw";
modalBg.style.height = "100vh";
modalBg.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
modalBg.style.display = "none";
modalBg.id = "modal-bg";
const closeModal = (): void => {
cta.close();
modalBg.style.display = "none";
const nhcarriganHeadersModalBg = document.createElement("div");
nhcarriganHeadersModalBg.style.zIndex = "4999";
nhcarriganHeadersModalBg.style.position = "fixed";
nhcarriganHeadersModalBg.style.top = "0";
nhcarriganHeadersModalBg.style.left = "0";
nhcarriganHeadersModalBg.style.width = "100vw";
nhcarriganHeadersModalBg.style.height = "100vh";
nhcarriganHeadersModalBg.style.backgroundColor = "rgba(0, 0, 0, 0.5)";
nhcarriganHeadersModalBg.style.display = "none";
nhcarriganHeadersModalBg.id = "modal-bg";
const nhcarriganHeadersCloseModal = (): void => {
nhcarriganHeadersCta.close();
nhcarriganHeadersModalBg.style.display = "none";
};
const handleModalClick = (event: MouseEvent): void => {
const nhcarriganHeadersHandleModalClick = (event: MouseEvent): void => {
event.stopPropagation();
if (event.target === cta) {
closeModal();
if (event.target === nhcarriganHeadersCta) {
nhcarriganHeadersCloseModal();
}
};
const showModal = (): void => {
const lastShown = Number.parseInt(
const nhcarriganHeadersShowModal = (): void => {
const nhcarriganHeadersLastShown = Number.parseInt(
localStorage.getItem("naomi-community-cta") ?? "0",
10,
);
const lastShownDate = new Date(lastShown);
const diff = Date.now() - lastShownDate.getTime();
console.table({ diff, lastShown, lastShownDate });
const nhcarriganHeadersLastShownDate = new Date(nhcarriganHeadersLastShown);
const nhcarriganHeadersDiff
= Date.now() - nhcarriganHeadersLastShownDate.getTime();
console.table({
diff: nhcarriganHeadersDiff,
lastShown: nhcarriganHeadersLastShown,
lastShownDate: nhcarriganHeadersLastShownDate,
});
// We only want to show this once a week.
if (diff < 1000 * 60 * 60 * 24 * 7) {
if (nhcarriganHeadersDiff < 1000 * 60 * 60 * 24 * 7) {
return;
}
cta.showModal();
modalBg.style.display = "block";
modalBg.addEventListener("click", closeModal);
const closeButton = cta.querySelector("button");
closeButton?.addEventListener("click", closeModal);
cta.addEventListener("click", handleModalClick);
nhcarriganHeadersCta.showModal();
nhcarriganHeadersModalBg.style.display = "block";
nhcarriganHeadersModalBg.addEventListener(
"click",
nhcarriganHeadersCloseModal,
);
const nhcarriganHeadersCloseButton
= nhcarriganHeadersCta.querySelector("button");
nhcarriganHeadersCloseButton?.addEventListener(
"click",
nhcarriganHeadersCloseModal,
);
nhcarriganHeadersCta.addEventListener(
"click",
nhcarriganHeadersHandleModalClick,
);
localStorage.setItem("naomi-community-cta", Date.now().toString());
};
body?.appendChild(cta);
body?.appendChild(modalBg);
showModal();
nhcarriganHeadersBody?.appendChild(nhcarriganHeadersCta);
nhcarriganHeadersBody?.appendChild(nhcarriganHeadersModalBg);
nhcarriganHeadersShowModal();