Skip to content

Commit 95c9198

Browse files
authored
Modernize build tooling and CSS (#76)
* Modernize build tooling and CSS * Add workspace metadata * Fix npm ci lockfile
1 parent bea38c4 commit 95c9198

21 files changed

Lines changed: 4071 additions & 18053 deletions

.claude/CLAUDE.md

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# Ultracite Code Standards
2+
3+
This project uses **Ultracite**, a zero-config preset that enforces strict code quality standards through automated formatting and linting.
4+
5+
## Quick Reference
6+
7+
- **Format code**: `npm exec -- ultracite fix`
8+
- **Check for issues**: `npm exec -- ultracite check`
9+
- **Diagnose setup**: `npm exec -- ultracite doctor`
10+
11+
Biome (the underlying engine) provides robust linting and formatting. Most issues are automatically fixable.
12+
13+
---
14+
15+
## Core Principles
16+
17+
Write code that is **accessible, performant, type-safe, and maintainable**. Focus on clarity and explicit intent over brevity.
18+
19+
### Type Safety & Explicitness
20+
21+
- Use explicit types for function parameters and return values when they enhance clarity
22+
- Prefer `unknown` over `any` when the type is genuinely unknown
23+
- Use const assertions (`as const`) for immutable values and literal types
24+
- Leverage TypeScript's type narrowing instead of type assertions
25+
- Use meaningful variable names instead of magic numbers - extract constants with descriptive names
26+
27+
### Modern JavaScript/TypeScript
28+
29+
- Use arrow functions for callbacks and short functions
30+
- Prefer `for...of` loops over `.forEach()` and indexed `for` loops
31+
- Use optional chaining (`?.`) and nullish coalescing (`??`) for safer property access
32+
- Prefer template literals over string concatenation
33+
- Use destructuring for object and array assignments
34+
- Use `const` by default, `let` only when reassignment is needed, never `var`
35+
36+
### Async & Promises
37+
38+
- Always `await` promises in async functions - don't forget to use the return value
39+
- Use `async/await` syntax instead of promise chains for better readability
40+
- Handle errors appropriately in async code with try-catch blocks
41+
- Don't use async functions as Promise executors
42+
43+
### React & JSX
44+
45+
- Use function components over class components
46+
- Call hooks at the top level only, never conditionally
47+
- Specify all dependencies in hook dependency arrays correctly
48+
- Use the `key` prop for elements in iterables (prefer unique IDs over array indices)
49+
- Nest children between opening and closing tags instead of passing as props
50+
- Don't define components inside other components
51+
- Use semantic HTML and ARIA attributes for accessibility:
52+
- Provide meaningful alt text for images
53+
- Use proper heading hierarchy
54+
- Add labels for form inputs
55+
- Include keyboard event handlers alongside mouse events
56+
- Use semantic elements (`<button>`, `<nav>`, etc.) instead of divs with roles
57+
58+
### Error Handling & Debugging
59+
60+
- Remove `console.log`, `debugger`, and `alert` statements from production code
61+
- Throw `Error` objects with descriptive messages, not strings or other values
62+
- Use `try-catch` blocks meaningfully - don't catch errors just to rethrow them
63+
- Prefer early returns over nested conditionals for error cases
64+
65+
### Code Organization
66+
67+
- Keep functions focused and under reasonable cognitive complexity limits
68+
- Extract complex conditions into well-named boolean variables
69+
- Use early returns to reduce nesting
70+
- Prefer simple conditionals over nested ternary operators
71+
- Group related code together and separate concerns
72+
73+
### Security
74+
75+
- Add `rel="noopener"` when using `target="_blank"` on links
76+
- Avoid `dangerouslySetInnerHTML` unless absolutely necessary
77+
- Don't use `eval()` or assign directly to `document.cookie`
78+
- Validate and sanitize user input
79+
80+
### Performance
81+
82+
- Avoid spread syntax in accumulators within loops
83+
- Use top-level regex literals instead of creating them in loops
84+
- Prefer specific imports over namespace imports
85+
- Avoid barrel files (index files that re-export everything)
86+
- Use proper image components (e.g., Next.js `<Image>`) over `<img>` tags
87+
88+
### Framework-Specific Guidance
89+
90+
**Next.js:**
91+
- Use Next.js `<Image>` component for images
92+
- Use `next/head` or App Router metadata API for head elements
93+
- Use Server Components for async data fetching instead of async Client Components
94+
95+
**React 19+:**
96+
- Use ref as a prop instead of `React.forwardRef`
97+
98+
**Solid/Svelte/Vue/Qwik:**
99+
- Use `class` and `for` attributes (not `className` or `htmlFor`)
100+
101+
---
102+
103+
## Testing
104+
105+
- Write assertions inside `it()` or `test()` blocks
106+
- Avoid done callbacks in async tests - use async/await instead
107+
- Don't use `.only` or `.skip` in committed code
108+
- Keep test suites reasonably flat - avoid excessive `describe` nesting
109+
110+
## When Biome Can't Help
111+
112+
Biome's linter will catch most issues automatically. Focus your attention on:
113+
114+
1. **Business logic correctness** - Biome can't validate your algorithms
115+
2. **Meaningful naming** - Use descriptive names for functions, variables, and types
116+
3. **Architecture decisions** - Component structure, data flow, and API design
117+
4. **Edge cases** - Handle boundary conditions and error states
118+
5. **User experience** - Accessibility, performance, and usability considerations
119+
6. **Documentation** - Add comments for complex logic, but prefer self-documenting code
120+
121+
---
122+
123+
Most formatting and common issues are automatically fixed by Biome. Run `npm exec -- ultracite fix` before committing to ensure compliance.

.github/workflows/ci.yml

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
name: CI
2+
3+
on:
4+
push:
5+
branches: ["**"]
6+
pull_request:
7+
8+
jobs:
9+
build:
10+
runs-on: ubuntu-latest
11+
steps:
12+
- name: Checkout
13+
uses: actions/checkout@v4
14+
- name: Setup Node
15+
uses: actions/setup-node@v4
16+
with:
17+
node-version: 'lts/*'
18+
cache: npm
19+
- name: Install
20+
run: npm ci
21+
- name: Lint
22+
run: npm run lint
23+
- name: Format Check
24+
run: npm run format:check
25+
- name: Build
26+
run: npm run build

.husky/pre-commit

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
npm test
2+
3+
#!/bin/sh
4+
# Exit on any error
5+
set -e
6+
7+
# Check if there are any staged files
8+
if [ -z "$(git diff --cached --name-only)" ]; then
9+
echo "No staged files to format"
10+
exit 0
11+
fi
12+
13+
# Store the hash of staged changes to detect modifications
14+
STAGED_HASH=$(git diff --cached | sha256sum | cut -d' ' -f1)
15+
16+
# Save list of staged files (handling all file states)
17+
STAGED_FILES=$(git diff --cached --name-only --diff-filter=ACMR)
18+
PARTIALLY_STAGED=$(git diff --name-only)
19+
20+
# Stash unstaged changes to preserve working directory
21+
# --keep-index keeps staged changes in working tree
22+
git stash push --quiet --keep-index --message "pre-commit-stash" || true
23+
STASHED=$?
24+
25+
# Run formatter on the staged files
26+
npx ultracite fix
27+
FORMAT_EXIT_CODE=$?
28+
29+
# Restore working directory state
30+
if [ $STASHED -eq 0 ]; then
31+
# Re-stage the formatted files
32+
if [ -n "$STAGED_FILES" ]; then
33+
echo "$STAGED_FILES" | while IFS= read -r file; do
34+
if [ -f "$file" ]; then
35+
git add "$file"
36+
fi
37+
done
38+
fi
39+
40+
# Restore unstaged changes
41+
git stash pop --quiet || true
42+
43+
# Restore partial staging if files were partially staged
44+
if [ -n "$PARTIALLY_STAGED" ]; then
45+
for file in $PARTIALLY_STAGED; do
46+
if [ -f "$file" ] && echo "$STAGED_FILES" | grep -q "^$file$"; then
47+
# File was partially staged - need to unstage the unstaged parts
48+
git restore --staged "$file" 2>/dev/null || true
49+
git add -p "$file" < /dev/null 2>/dev/null || git add "$file"
50+
fi
51+
done
52+
fi
53+
else
54+
# No stash was created, just re-add the formatted files
55+
if [ -n "$STAGED_FILES" ]; then
56+
echo "$STAGED_FILES" | while IFS= read -r file; do
57+
if [ -f "$file" ]; then
58+
git add "$file"
59+
fi
60+
done
61+
fi
62+
fi
63+
64+
# Check if staged files actually changed
65+
NEW_STAGED_HASH=$(git diff --cached | sha256sum | cut -d' ' -f1)
66+
if [ "$STAGED_HASH" != "$NEW_STAGED_HASH" ]; then
67+
echo "✨ Files formatted by Ultracite"
68+
fi
69+
70+
exit $FORMAT_EXIT_CODE

.zed/settings.json

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
{
2+
"formatter": "language_server",
3+
"format_on_save": "on",
4+
"lsp": {
5+
"typescript-language-server": {
6+
"settings": {
7+
"typescript": {
8+
"preferences": {
9+
"includePackageJsonAutoImports": "on"
10+
}
11+
}
12+
}
13+
}
14+
},
15+
"languages": {
16+
"JavaScript": {
17+
"formatter": {
18+
"language_server": {
19+
"name": "biome"
20+
}
21+
},
22+
"code_actions_on_format": {
23+
"source.fixAll.biome": true,
24+
"source.organizeImports.biome": true
25+
}
26+
},
27+
"TypeScript": {
28+
"formatter": {
29+
"language_server": {
30+
"name": "biome"
31+
}
32+
},
33+
"code_actions_on_format": {
34+
"source.fixAll.biome": true,
35+
"source.organizeImports.biome": true
36+
}
37+
},
38+
"TSX": {
39+
"formatter": {
40+
"language_server": {
41+
"name": "biome"
42+
}
43+
},
44+
"code_actions_on_format": {
45+
"source.fixAll.biome": true,
46+
"source.organizeImports.biome": true
47+
}
48+
}
49+
}
50+
}

AGENTS.md

Lines changed: 123 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,123 @@
1+
# Ultracite Code Standards
2+
3+
This project uses **Ultracite**, a zero-config preset that enforces strict code quality standards through automated formatting and linting.
4+
5+
## Quick Reference
6+
7+
- **Format code**: `npm exec -- ultracite fix`
8+
- **Check for issues**: `npm exec -- ultracite check`
9+
- **Diagnose setup**: `npm exec -- ultracite doctor`
10+
11+
Biome (the underlying engine) provides robust linting and formatting. Most issues are automatically fixable.
12+
13+
---
14+
15+
## Core Principles
16+
17+
Write code that is **accessible, performant, type-safe, and maintainable**. Focus on clarity and explicit intent over brevity.
18+
19+
### Type Safety & Explicitness
20+
21+
- Use explicit types for function parameters and return values when they enhance clarity
22+
- Prefer `unknown` over `any` when the type is genuinely unknown
23+
- Use const assertions (`as const`) for immutable values and literal types
24+
- Leverage TypeScript's type narrowing instead of type assertions
25+
- Use meaningful variable names instead of magic numbers - extract constants with descriptive names
26+
27+
### Modern JavaScript/TypeScript
28+
29+
- Use arrow functions for callbacks and short functions
30+
- Prefer `for...of` loops over `.forEach()` and indexed `for` loops
31+
- Use optional chaining (`?.`) and nullish coalescing (`??`) for safer property access
32+
- Prefer template literals over string concatenation
33+
- Use destructuring for object and array assignments
34+
- Use `const` by default, `let` only when reassignment is needed, never `var`
35+
36+
### Async & Promises
37+
38+
- Always `await` promises in async functions - don't forget to use the return value
39+
- Use `async/await` syntax instead of promise chains for better readability
40+
- Handle errors appropriately in async code with try-catch blocks
41+
- Don't use async functions as Promise executors
42+
43+
### React & JSX
44+
45+
- Use function components over class components
46+
- Call hooks at the top level only, never conditionally
47+
- Specify all dependencies in hook dependency arrays correctly
48+
- Use the `key` prop for elements in iterables (prefer unique IDs over array indices)
49+
- Nest children between opening and closing tags instead of passing as props
50+
- Don't define components inside other components
51+
- Use semantic HTML and ARIA attributes for accessibility:
52+
- Provide meaningful alt text for images
53+
- Use proper heading hierarchy
54+
- Add labels for form inputs
55+
- Include keyboard event handlers alongside mouse events
56+
- Use semantic elements (`<button>`, `<nav>`, etc.) instead of divs with roles
57+
58+
### Error Handling & Debugging
59+
60+
- Remove `console.log`, `debugger`, and `alert` statements from production code
61+
- Throw `Error` objects with descriptive messages, not strings or other values
62+
- Use `try-catch` blocks meaningfully - don't catch errors just to rethrow them
63+
- Prefer early returns over nested conditionals for error cases
64+
65+
### Code Organization
66+
67+
- Keep functions focused and under reasonable cognitive complexity limits
68+
- Extract complex conditions into well-named boolean variables
69+
- Use early returns to reduce nesting
70+
- Prefer simple conditionals over nested ternary operators
71+
- Group related code together and separate concerns
72+
73+
### Security
74+
75+
- Add `rel="noopener"` when using `target="_blank"` on links
76+
- Avoid `dangerouslySetInnerHTML` unless absolutely necessary
77+
- Don't use `eval()` or assign directly to `document.cookie`
78+
- Validate and sanitize user input
79+
80+
### Performance
81+
82+
- Avoid spread syntax in accumulators within loops
83+
- Use top-level regex literals instead of creating them in loops
84+
- Prefer specific imports over namespace imports
85+
- Avoid barrel files (index files that re-export everything)
86+
- Use proper image components (e.g., Next.js `<Image>`) over `<img>` tags
87+
88+
### Framework-Specific Guidance
89+
90+
**Next.js:**
91+
- Use Next.js `<Image>` component for images
92+
- Use `next/head` or App Router metadata API for head elements
93+
- Use Server Components for async data fetching instead of async Client Components
94+
95+
**React 19+:**
96+
- Use ref as a prop instead of `React.forwardRef`
97+
98+
**Solid/Svelte/Vue/Qwik:**
99+
- Use `class` and `for` attributes (not `className` or `htmlFor`)
100+
101+
---
102+
103+
## Testing
104+
105+
- Write assertions inside `it()` or `test()` blocks
106+
- Avoid done callbacks in async tests - use async/await instead
107+
- Don't use `.only` or `.skip` in committed code
108+
- Keep test suites reasonably flat - avoid excessive `describe` nesting
109+
110+
## When Biome Can't Help
111+
112+
Biome's linter will catch most issues automatically. Focus your attention on:
113+
114+
1. **Business logic correctness** - Biome can't validate your algorithms
115+
2. **Meaningful naming** - Use descriptive names for functions, variables, and types
116+
3. **Architecture decisions** - Component structure, data flow, and API design
117+
4. **Edge cases** - Handle boundary conditions and error states
118+
5. **User experience** - Accessibility, performance, and usability considerations
119+
6. **Documentation** - Add comments for complex logic, but prefer self-documenting code
120+
121+
---
122+
123+
Most formatting and common issues are automatically fixed by Biome. Run `npm exec -- ultracite fix` before committing to ensure compliance.

0 commit comments

Comments
 (0)