Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
23 changes: 23 additions & 0 deletions .claude/hooks/lint-on-save.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
#!/bin/bash
# Runs linters on files after Claude edits them

# Read hook input from stdin
input=$(cat)
file_path=$(echo "$input" | jq -r '.tool_input.file_path // empty')

# Exit silently if no file path
[ -z "$file_path" ] && exit 0

# Only lint specific file types
case "$file_path" in
*.js|*.jsx|*.ts|*.tsx|*.astro)
cd "$CLAUDE_PROJECT_DIR"
npm run lint:fix -- "$file_path" 2>/dev/null || true
;;
*.md|*.mdx)
cd "$CLAUDE_PROJECT_DIR"
npm run spellcheck 2>/dev/null || true
;;
esac

exit 0
15 changes: 15 additions & 0 deletions .claude/settings.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
{
"hooks": {
"PostToolUse": [
{
"matcher": "Write|Edit",
"hooks": [
{
"type": "command",
"command": "\"$CLAUDE_PROJECT_DIR\"/.claude/hooks/lint-on-save.sh"
}
]
}
]
}
}
57 changes: 57 additions & 0 deletions .github/workflows/claude-code-review.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
name: Claude Code Review

on:
pull_request:
types: [opened, synchronize]
# Optional: Only run on specific file changes
# paths:
# - "src/**/*.ts"
# - "src/**/*.tsx"
# - "src/**/*.js"
# - "src/**/*.jsx"

jobs:
claude-review:
# Optional: Filter by PR author
# if: |
# github.event.pull_request.user.login == 'external-contributor' ||
# github.event.pull_request.user.login == 'new-developer' ||
# github.event.pull_request.author_association == 'FIRST_TIME_CONTRIBUTOR'

runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write

steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Run Claude Code Review
id: claude-review
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}
prompt: |
REPO: ${{ github.repository }}
PR NUMBER: ${{ github.event.pull_request.number }}
Please review this pull request and provide feedback on:
- Code quality and best practices
- Potential bugs or issues
- Performance considerations
- Security concerns
- Test coverage
Use the repository's CLAUDE.md for guidance on style and conventions. Be constructive and helpful in your feedback.
Use `gh pr comment` with your Bash tool to leave your review as a comment on the PR.
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
# or https://docs.claude.com/en/docs/claude-code/cli-reference for available options
claude_args: '--allowed-tools "Bash(gh issue view:*),Bash(gh search:*),Bash(gh issue list:*),Bash(gh pr comment:*),Bash(gh pr diff:*),Bash(gh pr view:*),Bash(gh pr list:*)"'

50 changes: 50 additions & 0 deletions .github/workflows/claude.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
name: Claude Code

on:
issue_comment:
types: [created]
pull_request_review_comment:
types: [created]
issues:
types: [opened, assigned]
pull_request_review:
types: [submitted]

jobs:
claude:
if: |
(github.event_name == 'issue_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review_comment' && contains(github.event.comment.body, '@claude')) ||
(github.event_name == 'pull_request_review' && contains(github.event.review.body, '@claude')) ||
(github.event_name == 'issues' && (contains(github.event.issue.body, '@claude') || contains(github.event.issue.title, '@claude')))
runs-on: ubuntu-latest
permissions:
contents: read
pull-requests: read
issues: read
id-token: write
actions: read # Required for Claude to read CI results on PRs
steps:
- name: Checkout repository
uses: actions/checkout@v4
with:
fetch-depth: 1

- name: Run Claude Code
id: claude
uses: anthropics/claude-code-action@v1
with:
claude_code_oauth_token: ${{ secrets.CLAUDE_CODE_OAUTH_TOKEN }}

# This is an optional setting that allows Claude to read CI results on PRs
additional_permissions: |
actions: read
# Optional: Give a custom prompt to Claude. If this is not specified, Claude will perform the instructions specified in the comment that tagged it.
# prompt: 'Update the pull request description to include a summary of changes.'

# Optional: Add claude_args to customize behavior and configuration
# See https://github.com/anthropics/claude-code-action/blob/main/docs/usage.md
# or https://docs.claude.com/en/docs/claude-code/cli-reference for available options
# claude_args: '--allowed-tools Bash(gh pr:*)'

1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -26,3 +26,4 @@ pnpm-debug.log*

# CSpell cache
.cspellcache
/test-results
206 changes: 59 additions & 147 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,129 +4,82 @@ This file provides guidance to Claude Code (claude.ai/code) when working with co

## Repository Overview

This is an Astro-based static site deployed to GitHub Pages. The site uses Tailwind CSS for styling and is built using npm/Node.js, then deployed via GitHub Actions.
This is an Astro-based static site deployed to GitHub Pages. The site uses Tailwind CSS for styling and is built using npm/Node.js, then deployed via GitHub Actions; as such, any changes pushed to `main` will be automatically deployed—convenient, but be careful!

## Key Commands

### Local Development

The repository includes a justfile for convenient local development:

1. **Initial setup** (one-time):
```bash
# Install trop (port reservation tool)
# Required for automatic port management when running multiple repo checkouts
cargo install trop-cli

# Install dependencies
npm install
# OR using justfile
just install
```

**About trop**: This project uses [trop](https://github.com/plx/trop) for automatic port management. When you run `just preview`, trop automatically assigns a unique port for each repository checkout directory. This allows you to run multiple checkouts simultaneously without port conflicts. Each directory gets the same port consistently (idempotent), so your bookmarks and workflows remain stable.

2. **Development commands** (via justfile):
```bash
# Start Astro dev server for preview (port automatically allocated by trop)
just preview

# Start server and open in browser
just view

# Stop the server
just shutdown

# Open browser if server is already running
just open

# Build for production
just build

# Clean build artifacts
just clean

# Use custom port (all commands accept port argument)
just view 8080
just shutdown 8080
```

3. **Direct npm commands** (if needed):
```bash
# Run development server
npm run dev

# Build for production
npm run build

# Preview production build
npm run preview

# Lint code
npm run lint
npm run lint:fix
```

4. **GitHub Pages deployment**: Push changes to the main branch and GitHub Actions will automatically build and deploy via the `.github/workflows/deploy.yml` workflow

### Code Style and Linting

**IMPORTANT**: This project enforces strict linting rules via ESLint. Always run the linter before committing:

```bash
npm run lint # Check for linting errors
npm run lint:fix # Auto-fix linting errors where possible
```
The repository includes a justfile to gather all project commands in a single place; if you're unsure "how do I X?", look there first.
It also manages the preview server using a tool called `trop` (https://github.com/plx/trop).

Some key commands are:

- just install: installs dependencies (npm ci)
- just preview: launches dev server with hot reload (port automatically allocated by trop)
- just shutdown: kills dev server if running (port automatically allocated by trop)
- just build: builds the site for production (to dist/)
- just spellcheck: checks spelling in source files
- just spellcheck-html: checks spelling in built HTML output
- just lint: runs ESLint on all files
- just lint-fix: auto-fixes ESLint issues where possible
- just validate: runs all validation checks (lint + spellcheck + build + links)

## Key Technical Decisions

- **Framework**: Astro with React integration
- **Styling**: Tailwind CSS with Typography plugin
- **Content**: MDX support for enhanced markdown
- **Build**: Static site generation to `dist/` folder
- **Deployment**: GitHub Actions workflow deploys to GitHub Pages
- **Site URL**: https://plx.github.io

Additionally, we aim to have *reasonable* accessibility support throughout the site.

## Content Structure

The site's content is organized into three main collections:

- Blog posts (longer-form articles): `src/content/blog/`
- Briefs (short notes): `src/content/briefs/`
- Projects: `src/content/projects/`

Key linting requirements:
- **Use double quotes for strings** - NOT single quotes (TypeScript/JavaScript)
- The project uses ESLint with strict quote rules
- CI/CD will fail if linting errors are present
- Always verify with `npm run lint` before pushing changes
Here are brief remarks about each.

### Content Structure
### Blog Posts

Structured as folders containing *at least* an `index.md` file, placed in `src/content/blog/`; for example, `my-new-post` looks like:

#### Blog Posts
Create new blog posts in `src/content/blog/` as folders with an `index.md` file:
```
src/content/blog/my-new-post/
└── index.md
src/content/blog/my-new-post/index.md
```

Posts should include front matter with relevant metadata.

#### Briefs (Short Notes)
Create brief notes in category subfolders within `src/content/briefs/`:
```
src/content/briefs/swift-warts/my-swift-brief.md
src/content/briefs/claude-code/my-claude-brief.md
```
### Briefs (Short Notes)

Categories are auto-discovered from folder names. To add a new category, simply create a new folder. You can optionally add a `category.yaml` file in the folder to customize the category metadata (display name, description, sort priority).
Organized into categories represented as folders within `src/content/briefs/`, and stored *directly* as markdown files (no additional nesting / generic `index.md`).
For example, the following contains two briefs—one in the `swift-warts` category and one in the `claude-code` category:

#### Projects
Create project pages in `src/content/projects/` as folders with an `index.md` file:
```
src/content/projects/my-project/
└── index.md
src/content/briefs/swift-warts/my-swift-brief.md
src/content/briefs/claude-code/my-claude-brief.md
```

### Testing and QA
Categories are auto-discovered from folder names. To add a new category, simply create a new folder.
Categories may also customize their display name, description, and sort priority by establishing a `category.yaml` file in the category folder; this is useful because the category name is used in multiple places throughout the site, and benefits from having distinct, contextually-appropriate representations.

The repository has Playwright browser automation available via MCP for testing and QA purposes. This enables:
- Visual testing and screenshot capture
- Navigation testing
- Content verification
- Browser automation tasks
### Projects (Descriptions of Projects)

Note: the project has a dedicated QA-via-playwright agent named "web-qa-playwright".
Structured analogously to "Blog Posts`, but placed in `src/content/projects/`, instead.

## Architecture
## Directory Structure

### Directory Structure
- `src/`: Source code
- `components/`: Astro components
- `content/`: Content collections (blog, briefs, projects)
- `blog/`: where blog posts live
- `briefs/`: where briefs live
- `projects/`: where project pages live
- `layouts/`: Page layouts
- `pages/`: Routes and pages
- `styles/`: Global styles
Expand All @@ -135,52 +88,11 @@ Note: the project has a dedicated QA-via-playwright agent named "web-qa-playwrig
- `dist/`: Build output (generated, not in repo)
- `.github/workflows/`: GitHub Actions workflows

### Key Technical Details
- **Framework**: Astro with React integration
- **Styling**: Tailwind CSS with Typography plugin
- **Content**: MDX support for enhanced markdown
- **Build**: Static site generation to `dist/` folder
- **Deployment**: GitHub Actions workflow deploys to GitHub Pages
- **Site URL**: https://plx.github.io
## Testing and QA

The repository has Playwright browser automation available via MCP for testing and QA purposes. This enables:

### Content Collections
Astro's content collections are used to manage:
- Blog posts with metadata
- Brief notes
- Project pages

### Build & Deployment Flow
1. Content is written in Markdown/MDX files
2. Astro processes content through layouts and components
3. `npm run build` generates static site in `dist/` folder
4. GitHub Actions workflow triggers on push to main
5. Workflow builds site and deploys to GitHub Pages

### CSS Lessons Learned

When implementing mobile navigation, several CSS challenges were encountered and solved:

1. **Element Hiding Best Practices**
- **Issue**: Using negative `left` positioning (e.g., `left: -100%`) can leave partial elements visible
- **Solution**: Use `transform: translateX(-100%)` combined with `visibility: hidden` for complete hiding
- **Why**: Transform moves the element visually while visibility ensures it's not interactable

2. **Mobile Layout Gotchas**
- **Issue**: Flex layouts can cause unexpected spacing on mobile
- **Solution**: Change wrapper to `display: block` on mobile breakpoints
- **Why**: Removes flex-related spacing issues

3. **Z-Index and Positioning**
- Mobile header needs proper z-index stacking (1000+) to stay above content
- Fixed positioning requires careful height calculations for content padding
- Use `overflow: visible` on containers to allow menus to extend beyond

4. **Debugging Overlapping Elements**
- Browser developer tools are essential for identifying which specific element is causing overlap
- Check both the container and child elements for positioning issues
- Sometimes the issue is inherited padding/margin rather than the obvious element

5. **Full-Width Mobile Menus**
- Set menu width to 100% for better mobile readability
- Ensure no parent containers constrain the width
- Test on actual mobile devices or browser mobile emulation
- Visual testing and screenshot capture
- Navigation testing
- Content verification
- Browser automation tasks
Loading