Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
3215250
Add shared dark-mode core: constants and transform/config lib
markmead Jun 6, 2026
ad6eff1
Migrate consts.ts to src/constants/seo.js
markmead Jun 6, 2026
5a1c3e9
Refactor CLI to import from shared dark-mode core
markmead Jun 6, 2026
78c07f7
Add Tools link to site navigation
markmead Jun 6, 2026
1f1dbc6
Add Dark Mode Generator tool
markmead Jun 6, 2026
35d1cef
Fix shade map not being used during class transformation
markmead Jun 6, 2026
a1898b9
Merge branch 'main' into feature/dark-mode-generator
markmead Jun 6, 2026
ba559fc
Bugfix - Strip outer HTML from light preview to prevent dark class le…
markmead Jun 6, 2026
881ac3b
Bugfix - Force class-based dark variant in preview iframes
markmead Jun 6, 2026
bda2aca
Update - Default split preview, add rules examples dialog
markmead Jun 6, 2026
015659f
Update - Center rules dialog and add open/close animation
markmead Jun 6, 2026
d65bd70
Add explicit checks/braces; tidy DarkModeGenerator
markmead Jun 6, 2026
7dd1a96
Update - Restyle DarkModeGenerator UI and add tools index placeholders
markmead Jun 9, 2026
93a504b
Update - Normalise section header styles and dialog example formatting
markmead Jun 9, 2026
544955e
Update - Split DarkModeGenerator into focused components and modules
markmead Jun 9, 2026
ddce98b
Refactor DarkModeGenerator UI and preview docs
markmead Jun 9, 2026
bb4d9b1
Update - Extract side/preview panels and enforce two-word naming thro…
markmead Jun 9, 2026
da1e8a0
Update - Rename Dmg* components to DarkMode* prefix
markmead Jun 9, 2026
a21fb85
Update - Replace dmg- HTML id prefixes with dark-mode-
markmead Jun 9, 2026
879212b
Update - Align rule card accordion styling with side panel
markmead Jun 9, 2026
b0b82de
Update - Fix rule card content styles, update icons, move action butt…
markmead Jun 9, 2026
d630384
Update - Click-to-edit rule names, JS chevron rotation, close accordi…
markmead Jun 9, 2026
114aef1
Update - Rule field labels with separate hint text
markmead Jun 9, 2026
3db4234
Adjust dark-mode UI styling and input classes
markmead Jun 9, 2026
87f66c5
Update - Section headings in rule accordion use uppercase label style
markmead Jun 9, 2026
bc69509
Add - Plan for bottom-sheet rule inspector
markmead Jun 9, 2026
b82ceed
Show last-saved time in side panel
markmead Jun 10, 2026
7b8415f
Add - Bottom-sheet rule inspector replacing inline accordion fields
markmead Jun 10, 2026
386aac3
Fix - Use descriptive variable names in rule-renderer
markmead Jun 10, 2026
e5a0136
Update - Rule inspector UI with section headings and inline name editor
markmead Jun 10, 2026
7d51c37
Update - Inline name editor uses pencil button to enter edit mode
markmead Jun 10, 2026
ad2ce08
Fix - Address code review findings in rule inspector
markmead Jun 10, 2026
3d08dca
Update - Remove backward-compat guards and clean up rule inspector code
markmead Jun 10, 2026
0115dba
Update - Enforce descriptive variable names across dark mode files
markmead Jun 10, 2026
9631add
Update - Revert unnecessary variable renames
markmead Jun 10, 2026
38a67a6
Fix - Correct white/black color map and remove gray-900 special case
markmead Jun 10, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 8 additions & 0 deletions eslint.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export default defineConfig([
},
},
},

{
files: ['scripts/**'],
languageOptions: {
Expand All @@ -28,4 +29,11 @@ export default defineConfig([
},
},
},

{
rules: {
curly: ['error', 'all'],
eqeqeq: ['error', 'always'],
},
},
])
12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -15,23 +15,23 @@
},
"dependencies": {
"@astrojs/check": "^0.9.9",
"@astrojs/cloudflare": "^13.6.0",
"@astrojs/cloudflare": "^13.6.1",
"@astrojs/mdx": "5.0.4",
"@astrojs/rss": "^4.0.18",
"@astrojs/sitemap": "3.7.2",
"@lucide/astro": "^1.17.0",
"astro": "6.3.1",
"wrangler": "^4.95.0"
"wrangler": "^4.98.0"
},
"devDependencies": {
"@eslint/js": "^10.0.1",
"@playwright/test": "^1.60.0",
"@tailwindcss/cli": "^4.3.0",
"@tailwindcss/forms": "^0.5.11",
"@tailwindcss/typography": "^0.5.19",
"@tailwindcss/typography": "^0.5.20",
"@tailwindcss/vite": "^4.3.0",
"@types/node": "^25.9.1",
"eslint": "^10.4.0",
"@types/node": "^25.9.2",
"eslint": "^10.4.1",
"eslint-plugin-astro": "^1.7.0",
"globals": "^17.6.0",
"prettier": "^3.8.3",
Expand All @@ -40,7 +40,7 @@
"rehype-external-links": "^3.0.0",
"tailwindcss": "^4.3.0",
"typescript": "^6.0.3",
"typescript-eslint": "^8.60.0"
"typescript-eslint": "^8.61.0"
},
"overrides": {
"vite": "^7"
Expand Down
880 changes: 430 additions & 450 deletions pnpm-lock.yaml

Large diffs are not rendered by default.

128 changes: 3 additions & 125 deletions scripts/generate-dark-variants.js
Original file line number Diff line number Diff line change
Expand Up @@ -5,137 +5,15 @@ import { fileURLToPath } from 'url'
import fs from 'fs'
import path from 'path'

const SHADE_MAP = {
50: 800,
100: 800,
200: 700,
300: 600,
400: 500,
500: 400,
600: 300,
700: 200,
800: 100,
900: 50,
}

const COLOR_MAP = {
white: 'gray-900',
black: 'white',
}

const COLOR_FAMILIES = [
'red',
'orange',
'amber',
'yellow',
'lime',
'green',
'emerald',
'teal',
'cyan',
'sky',
'blue',
'indigo',
'violet',
'purple',
'fuchsia',
'pink',
'rose',
'slate',
'gray',
'zinc',
'neutral',
'stone',
]
import { DEFAULT_CONFIG } from '../src/lib/dark-mode/config.js'
import { transformHtmlString } from '../src/lib/dark-mode/transform-html.js'

function getProjectRoot() {
const scriptFilePath = fileURLToPath(import.meta.url)

return path.resolve(path.dirname(scriptFilePath), '..')
}

function splitVariantPrefix(className) {
const lastSeparatorIndex = className.lastIndexOf(':')

if (lastSeparatorIndex === -1) {
return {
variantPrefix: '',
classWithoutVariant: className,
}
}

return {
variantPrefix: className.slice(0, lastSeparatorIndex + 1),
classWithoutVariant: className.slice(lastSeparatorIndex + 1),
}
}

function transformClass(className) {
const { variantPrefix, classWithoutVariant } = splitVariantPrefix(className)

const gray900Match = classWithoutVariant.match(/^(.*?)(gray-900)(\/\d+)?$/)

if (gray900Match) {
const grayPrefix = gray900Match[1] ?? ''
const graySuffix = gray900Match[3] ?? ''

return `${className} dark:${variantPrefix}${grayPrefix}white${graySuffix}`
}

for (const [lightColor, darkColor] of Object.entries(COLOR_MAP)) {
if (classWithoutVariant.includes(lightColor)) {
const colorMatch = classWithoutVariant.match(/^((?:[\w]+-)*)(white|black)(\/\d+)?$/)

if (colorMatch) {
const colorPrefix = colorMatch[1] ?? ''
const colorSuffix = colorMatch[3] ?? ''
const darkClass = `${colorPrefix}${darkColor}${colorSuffix}`

return `${className} dark:${variantPrefix}${darkClass}`
}
}
}

for (const colorFamily of COLOR_FAMILIES) {
const colorRegex = new RegExp(`^([\\w-]*?)${colorFamily}-(\\d+)(.*?)$`)
const colorMatch = classWithoutVariant.match(colorRegex)

if (colorMatch) {
const colorPrefix = colorMatch[1] ?? ''
const colorShade = colorMatch[2] ?? '0'
const colorSuffix = colorMatch[3] ?? ''
const shadeNum = parseInt(colorShade, 10)

if (shadeNum in SHADE_MAP) {
const darkShade = SHADE_MAP[shadeNum]
const darkClass = `${colorPrefix}${colorFamily}-${darkShade}${colorSuffix}`

return `${className} dark:${variantPrefix}${darkClass}`
}
}
}

return className
}

function transformClassAttribute(classAttr) {
if (!classAttr) return classAttr

return classAttr
.split(/\s+/)
.filter(Boolean)
.map((className) => (className.includes('dark:') ? className : transformClass(className)))
.join(' ')
}

function transformHtmlContent(htmlContent) {
return htmlContent.replace(/class="([^"]*)"/g, (_, classAttr) => {
const transformedClass = transformClassAttribute(classAttr)

return `class="${transformedClass}"`
})
}

function isPathWithinBounds(targetPath, allowedParent) {
const normalizedTarget = path.normalize(path.resolve(targetPath))
const normalizedParent = path.normalize(path.resolve(allowedParent))
Expand Down Expand Up @@ -244,7 +122,7 @@ function processFolder() {

try {
const lightContent = fs.readFileSync(lightPath, 'utf-8')
const darkContent = transformHtmlContent(lightContent)
const darkContent = transformHtmlString(lightContent, DEFAULT_CONFIG)

fs.writeFileSync(darkPath, darkContent, 'utf-8')

Expand Down
2 changes: 1 addition & 1 deletion src/components/BaseHead.astro
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { Font } from 'astro:assets'

import OgImage from '../assets/og.jpg'

import { SITE_TITLE } from '../consts'
import { SITE_TITLE } from '../constants/seo'

import type { ImageMetadata } from 'astro'

Expand Down
Loading