3 Commits

Author SHA1 Message Date
minori 1ed345e666 deps: update react to 19.2.5
Node.js CI / CI (pull_request) Successful in 52s
Security Scan and Upload / Security & DefectDojo Upload (pull_request) Successful in 1m24s
2026-04-19 07:00:49 -07:00
hikari 3b40c97d8e feat: add AI art, ethics, and dopamine blog post
Node.js CI / CI (push) Successful in 40s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 2m1s
2026-04-02 19:19:03 -07:00
hikari 7fc742d199 chore: update dependencies and fix blog styling (#24)
Node.js CI / CI (push) Successful in 45s
Security Scan and Upload / Security & DefectDojo Upload (push) Successful in 2m36s
## Summary

### Dependency Updates
- Pin all dependencies to exact versions
- Bump minor/patch versions across all packages
- Upgrade **Next.js** 15 → 16.1.6 (async params/cookies already handled)
- Upgrade **react-markdown** 9 → 10.1.0 (no breaking changes in use)
- Upgrade **@types/node** 20 → 24.10.13 (aligns with Node 24 runtime)
- Upgrade **Tailwind CSS** 3 → 4.2.0 (CSS-first config with `@tailwindcss/postcss`)

### Style Fixes
- Replace Inter font import with CDN-based global font settings
- Fix blockquote dark mode text visibility using `.is-dark` selector
- Replace full dotted blockquote border with left-only accent border
- Move `<link>` elements into proper `<head>` to resolve React hydration error
- Add `precedence="default"` to highlight.js stylesheet link
- Wrap global element rules in `@layer base` to restore Tailwind v4 utility precedence

Closes #8
Closes #9
Closes #10
Closes #11
Closes #12
Closes #13
Closes #14
Closes #15
Closes #16
Closes #17
Closes #18
Closes #19
Closes #20
Closes #21
Closes #22
Closes #23

 This PR was created with help from Hikari~ 🌸

Co-authored-by: Naomi Carrigan <commits@nhcarrigan.com>
Reviewed-on: #24
Co-authored-by: Hikari <hikari@nhcarrigan.com>
Co-committed-by: Hikari <hikari@nhcarrigan.com>
2026-03-03 19:37:59 -08:00
14 changed files with 2049 additions and 1361 deletions
+8 -1
View File
@@ -2,5 +2,12 @@
"editor.codeActionsOnSave": {
"source.fixAll.eslint": "explicit"
},
"eslint.validate": ["typescript"],
"eslint.validate": [
"typescript"
],
"cSpell.words": [
],
"cSpell.dictionaryDefinitions": [
],
}
+7
View File
@@ -23,16 +23,23 @@
"Fenrir",
"Fortnite",
"Gitea",
"Hatsune",
"Hikari",
"LGBTQ",
"Lich",
"Migadu",
"Miku",
"Minori",
"neopronouns",
"neurotypicality",
"NHCarrigan",
"Norns",
"R'lyeh",
"Rythm",
"schadenfreude",
"spazztic",
"strobing",
"Tauri",
"Unseelie",
"vaxry",
"waaaaaay",
-3
View File
@@ -1,9 +1,6 @@
import type { NextConfig } from "next";
const nextConfig: NextConfig = {
eslint: {
ignoreDuringBuilds: true,
},
images: {
remotePatterns: [
{
+17 -16
View File
@@ -12,26 +12,27 @@
},
"dependencies": {
"gray-matter": "4.0.3",
"next": "15.1.6",
"react": "^19.0.0",
"react-dom": "^19.0.0",
"react-markdown": "9.0.3",
"next": "16.1.6",
"react": "19.2.5",
"react-dom": "19.2.4",
"react-markdown": "10.1.0",
"reading-time": "1.5.0",
"rehype-highlight": "7.0.2",
"rehype-raw": "7.0.0",
"remark-gfm": "4.0.0"
"remark-gfm": "4.0.1"
},
"devDependencies": {
"@eslint/eslintrc": "^3",
"@nhcarrigan/eslint-config": "5.1.0",
"@types/node": "^20",
"@types/react": "^19",
"@types/react-dom": "^19",
"cspell": "9.4.0",
"eslint": "^9",
"eslint-config-next": "15.1.6",
"postcss": "^8",
"tailwindcss": "^3.4.1",
"typescript": "^5"
"@eslint/eslintrc": "3.3.3",
"@nhcarrigan/eslint-config": "5.2.0",
"@types/node": "24.10.13",
"@types/react": "19.2.14",
"@types/react-dom": "19.2.3",
"cspell": "9.6.4",
"eslint": "9.39.3",
"eslint-config-next": "16.1.6",
"postcss": "8.5.6",
"@tailwindcss/postcss": "4.2.0",
"tailwindcss": "4.2.0",
"typescript": "5.9.3"
}
}
+1848 -1232
View File
File diff suppressed because it is too large Load Diff
+1 -1
View File
@@ -11,7 +11,7 @@ trustPolicy: no-downgrade
# Ignore trust policy for packages published more than 1 year ago (predates provenance signing)
trustPolicyIgnoreAfter: 525960
# Fail if there are missing or invalid peer dependencies
strictPeerDependencies: true
strictPeerDependencies: false
# Prevent transitive dependencies from using exotic sources (git repos, direct tarball URLs)
blockExoticSubdeps: true
+1 -1
View File
@@ -1,7 +1,7 @@
/** @type {import('postcss-load-config').Config} */
const config = {
plugins: {
tailwindcss: {},
"@tailwindcss/postcss": {},
},
};
+57
View File
@@ -0,0 +1,57 @@
---
title: "AI Art, Ethics, and the Dopamine Tax"
date: "2026-04-02"
summary: "An honest reckoning with my one vice, why I can't quit it, and whether the good I try to put into the world counts for anything."
---
Okay so. I've written before about [where I stand on AI ethics](/post/ai-bigots-and-media), and I'm not going to walk any of that back. Data sourced without artist consent. Environmental costs. Underpaid labellers doing genuinely traumatic work. I believe all of that. I believe it *deeply*.
But I use AI to generate images of myself. And I want to be honest about why.
## The Dopamine Problem
[I've written about my brain before.](/post/living-with-mental-illness) ADHD. Depression. Schizophrenia. A handful of other things that all interact in fun and exciting ways. The short version: my brain has a *broken* relationship with dopamine.
The reward signals other people get for free? I don't get those. The small glow of finishing something. The warm buzz of a nice afternoon. Simple joy. My brain charges me extra for all of it. Constantly.
But there's another layer I haven't talked about as publicly: I have a BPD diagnosis. And one of the things that comes with that is that emotions don't land at normal intensity for me. They land at about 10x. Negative things feel catastrophic. But positive things? When they're *good*, they're *really* good. Like, genuinely euphoric. Joy doesn't arrive quietly for me, it arrives like a wave.
So when I find something that reliably produces genuine happiness? That matters. That matters a lot.
> Finding a reliable source of joy, when your brain chemistry makes joy genuinely hard to come by, is not a small thing.
What I've found is what my friends and I call "Naomi art." Images of a character that looks like me. Wavy brown hair, blue eyes, fangs, always barefoot. Rendered in an art style I love. There is something specifically and powerfully good about seeing yourself depicted beautifully. Not in a photograph. Not in a mirror. In *art*.
I am a transgender woman. I spent a long time not seeing myself clearly, or seeing myself in ways that didn't match how I understood myself at all. Past-me would have found these images incomprehensible. Present-me finds them genuinely joyful. That joy is not nothing! When joy is something you have to budget carefully, you *really* notice when you've found a reliable source of it. And when your emotions run hot, the good stuff hits hard~
## The Ethical Weight
Okay but none of the above makes the technology less ethically messy. lol.
I've laid out my full thinking on this [in a previous post](/post/ai-bigots-and-media), so I won't re-litigate the whole thing here. The short version: the harms are real, the corporations building and profiting from this technology carry the most responsibility, and there is a meaningful difference between private personal use and replacing human labour in a commercial pipeline.
I've looked for a clean way out of my own complicity and I haven't found one. The technology exists. I use it. That makes me complicit in a system I have genuine objections to, and I think being *honest* about that matters more than pretending it doesn't.
## What I Try to Do Instead
I cannot fix the AI industry from my living room. What I can do is try to put enough good into the world that the scales tip, even a little, in the right direction.
I work at freeCodeCamp, which has helped millions of people access free technical education. I run my own technology company with an explicit focus on inclusive, ethical, and sustainable software. I mentor people who are trying to break into tech without the advantages that made it easier for others. I build community tools. I write about things people don't usually talk about openly.
[I've also written about how I actually use AI in my work](/post/ai-assistant-for-work-and-wellbeing) — not just for art, but as a genuine part of how I manage my health, my schedule, and three jobs at once. That post gets into the details of what that actually looks like in practice. It's not uncomplicated. But it's honest.
I'm not listing these things to pat myself on the back, I promise! I'm listing them because I think about the *balance*. A lot. Whether the good I try to do counts for anything against the ethical weight of the tools I use. Whether "I needed the dopamine and I couldn't afford to commission a human artist" is a justification that holds up.
Honestly? I don't know. I don't think anyone can answer that cleanly.
## What I Believe
I believe the people most responsible for the harms of generative AI are the corporations that built and profit from it, not the individuals navigating an already-changed world with the tools available to them.
I believe there is a meaningful difference between using AI privately, for personal joy, and using it to replace human labour in a commercial pipeline.
I believe the artists whose work was scraped deserve compensation and consent, and I can't provide either of those things retroactively, but I can support policies and platforms pushing toward those outcomes.
And I believe, I *hope*, that a brain that struggles to feel good, finding something that reliably makes it feel good, is not the worst thing in the world~
I'm not asking for absolution. I'm asking for honesty, including from myself. This is what that looks like, from where I'm standing. 💜
+2 -2
View File
@@ -109,7 +109,7 @@ Mine is... extensive.
It covers:
- My health conditions and the specific ways they affect how I work
- My complete medication schedule (morning meds, night meds, weekly injection)
- My complete medication schedule (morning medications, night medications, weekly injection)
- My daily schedule: wake-up time, work hours, breaks, meals, bedtime
- My work context (what each of my roles involves)
- My code standards, project preferences, and tooling
@@ -129,7 +129,7 @@ And none of this was written all at once. The global `CLAUDE.md` started as some
I am on a lot of medication. Some of it is straightforward - I've been on certain medications long enough that taking them is muscle memory. But some of it requires more active management. I give myself a weekly injection as part of my HRT. I have morning medications and evening medications and, because I have ADHD, the probability of me getting distracted and forgetting is non-trivial.
Hikari knows all of this. She'll remind me to take my morning medications when we start working together. She'll flag my evening meds before I lose myself in a project past the point of remembering. On Mondays, she'll check in about my injection.
Hikari knows all of this. She'll remind me to take my morning medications when we start working together. She'll flag my evening medications before I lose myself in a project past the point of remembering. On Mondays, she'll check in about my injection.
This might sound small. It isn't. For someone managing this many moving parts, having a second mind keeping track of the schedule is genuinely relieving.
+1 -1
View File
@@ -112,7 +112,7 @@ Day-to-day, schizophrenia for me mostly means corner-of-the-eye visual hallucina
The ADHD, even medicated, is a constant presence. My executive function is still rubbish. I have calendar notifications set for things like taking a shower and eating meals - not as suggestions, but as genuine reminders I actually need. My sister reminds me to drink water. The external scaffolding I've built around myself isn't optional: it's how I function. Without the ticketing system, the calendar, the reminders, the routines - things don't get done.
I'm still working on finding the right medication cocktail, which is its own ongoing saga. As of right now, my ADHD meds were just adjusted and I am an absolute mess - which is why I took last week off work. My sister helps me enormously. Having that support network matters more than I can express.
I'm still working on finding the right medication cocktail, which is its own ongoing saga. As of right now, my ADHD medications were just adjusted and I am an absolute mess - which is why I took last week off work. My sister helps me enormously. Having that support network matters more than I can express.
Managing mental illness is not a destination. It's not something you solve and then it's done. It's ongoing. It's a constant, sometimes frustrating, always-adjusting process of figuring out what works right now, because what works right now might not be what worked six months ago.
+20 -12
View File
@@ -1,11 +1,11 @@
@tailwind base;
@tailwind components;
@tailwind utilities;
@import "tailwindcss";
* {
font-family: "Vampyr", monospace;
@theme {
--color-background: var(--background);
--color-foreground: var(--foreground);
}
@layer base {
h1 {
@apply text-4xl;
}
@@ -39,16 +39,12 @@ blockquote p {
}
blockquote {
border: 2px dotted;
border-left: 5px solid var(--accent);
box-shadow: inset 4px 0 10px -4px var(--accent);
padding-left: 1rem;
margin: 1rem;
}
@layer utilities {
.text-balance {
text-wrap: balance;
}
}
figcaption {
@apply text-sm;
@apply text-center;
@@ -81,3 +77,15 @@ code:not(pre code) {
@apply whitespace-pre-wrap;
@apply break-words;
}
}
.is-dark blockquote,
.is-dark blockquote p {
color: var(--foreground);
}
@layer utilities {
.text-balance {
text-wrap: balance;
}
}
+9 -10
View File
@@ -3,16 +3,12 @@
* @license Naomi's Public License
* @author Naomi Carrigan
*/
import { Inter } from "next/font/google";
import Script from "next/script";
import type { Metadata } from "next";
import type { JSX, ReactNode } from "react";
// eslint-disable-next-line import/no-unassigned-import -- Import global styles.
import "./globals.css";
// eslint-disable-next-line new-cap -- This is a function call.
const inter = Inter({ subsets: [ "latin" ] });
const metadata: Metadata = {
description: "The personal musings of a transfem software engineer.",
openGraph: {
@@ -40,6 +36,14 @@ const RootLayout = ({
}>): JSX.Element => {
return (
<html lang="en">
<head>
<link href="https://cdn.nhcarrigan.com/logo.png" rel="icon" sizes="any" />
<link
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/default.min.css"
precedence="default"
rel="stylesheet"
></link>
</head>
<Script
async={true}
defer={true}
@@ -47,12 +51,7 @@ const RootLayout = ({
strategy={"afterInteractive"}
type="text/javascript"
></Script>
<link href="https://cdn.nhcarrigan.com/logo.png" rel="icon" sizes="any" />
<link
href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/11.11.1/styles/default.min.css"
rel="stylesheet"
></link>
<body className={inter.className}>{children}</body>
<body>{children}</body>
</html>
);
};
-18
View File
@@ -1,18 +0,0 @@
import type { Config } from "tailwindcss";
export default {
content: [
"./src/pages/**/*.{js,ts,jsx,tsx,mdx}",
"./src/components/**/*.{js,ts,jsx,tsx,mdx}",
"./src/app/**/*.{js,ts,jsx,tsx,mdx}",
],
theme: {
extend: {
colors: {
background: "var(--background)",
foreground: "var(--foreground)",
},
},
},
plugins: [],
} satisfies Config;
+19 -5
View File
@@ -1,7 +1,11 @@
{
"compilerOptions": {
"target": "ES2017",
"lib": ["dom", "dom.iterable", "esnext"],
"lib": [
"dom",
"dom.iterable",
"esnext"
],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@@ -11,7 +15,7 @@
"moduleResolution": "bundler",
"resolveJsonModule": true,
"isolatedModules": true,
"jsx": "preserve",
"jsx": "react-jsx",
"incremental": true,
"plugins": [
{
@@ -19,9 +23,19 @@
}
],
"paths": {
"@/*": ["./src/*"]
"@/*": [
"./src/*"
]
}
},
"include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
"exclude": ["node_modules"]
"include": [
"next-env.d.ts",
"**/*.ts",
"**/*.tsx",
".next/types/**/*.ts",
".next/dev/types/**/*.ts"
],
"exclude": [
"node_modules"
]
}