skillby rohunvora
html-tools
This skill should be used when building single-file browser applications, client-side utilities, or interactive tools that need no backend. Covers patterns for state persistence, API integration, and advanced browser capabilities without React or build steps.
Installs: 0
Used in: 1 repos
Updated: 1d ago
$
npx ai-builder add skill rohunvora/html-toolsInstalls to .claude/skills/html-tools/
# HTML Tools
Build single-file applications combining HTML, CSS, and JavaScript that deliver useful functionality without build steps, frameworks, or servers.
Based on Simon Willison's patterns from building 150+ such tools.
## When to Use
- "Build me a tool that..."
- "Create a simple app to..."
- "Make a utility for..."
- Client-side data processing or visualization
- Tools that should work offline or be easily shared
- Prototypes that need to ship fast
## When to Skip
- Apps requiring authentication or persistent server storage
- Complex state management across many components
- Projects where React/Vue ecosystem benefits outweigh simplicity
## Core Principles
1. **Single file** — Inline CSS and JavaScript. One HTML file = complete tool.
2. **No React** — Vanilla JS or lightweight libraries only. Explicitly prompt "No React."
3. **No build step** — Load dependencies from CDNs, not npm.
4. **Small codebase** — Target a few hundred lines. If larger, reconsider scope.
## Development Workflow
### Starting a New Tool
1. Begin with Claude Artifacts, ChatGPT Canvas, or similar for rapid prototyping
2. Prompt explicitly: "Build this as a single HTML file. No React. No build steps."
3. For complex tools, use Claude Code or similar coding agents
4. Host on GitHub Pages for permanence
### Iterating
1. Remix existing tools as starting points
2. Record LLM transcripts in commit messages for documentation
3. Build debugging tools (clipboard viewer, CORS tester) to understand browser capabilities
## State Persistence Patterns
### URL State (for shareable links)
Store configuration in URL hash or query params. Users can bookmark or share exact state.
```javascript
// Save state to URL
function saveState(state) {
const params = new URLSearchParams(state);
history.replaceState(null, '', '?' + params.toString());
}
// Load state from URL
function loadState() {
return Object.fromEntries(new URLSearchParams(location.search));
}
// On page load
const state = loadState();
if (state.color) applyColor(state.color);
```
### localStorage (for secrets and large data)
Store API keys and user preferences without server exposure.
```javascript
// Store API key securely (never in URL)
localStorage.setItem('openai_api_key', key);
// Retrieve
const key = localStorage.getItem('openai_api_key');
// Prompt user if missing
if (!key) {
key = prompt('Enter your OpenAI API key:');
localStorage.setItem('openai_api_key', key);
}
```
## Data Input/Output Patterns
### Copy-Paste as Data Transfer
Treat clipboard as the universal data bus. Always include "Copy to clipboard" buttons.
```javascript
async function copyToClipboard(text) {
await navigator.clipboard.writeText(text);
showToast('Copied!');
}
// Rich clipboard (multiple formats)
async function copyRich(html, text) {
const blob = new Blob([html], { type: 'text/html' });
const item = new ClipboardItem({
'text/html': blob,
'text/plain': new Blob([text], { type: 'text/plain' })
});
await navigator.clipboard.write([item]);
}
```
### File Input Without Server
Accept file uploads that process entirely client-side.
```html
<input type="file" id="fileInput" accept=".json,.csv,.txt">
<script>
document.getElementById('fileInput').addEventListener('change', async (e) => {
const file = e.target.files[0];
const text = await file.text();
processFile(text, file.name);
});
</script>
```
### File Download Without Server
Generate and download files entirely in browser.
```javascript
function downloadFile(content, filename, mimeType = 'text/plain') {
const blob = new Blob([content], { type: mimeType });
const url = URL.createObjectURL(blob);
const a = document.createElement('a');
a.href = url;
a.download = filename;
a.click();
URL.revokeObjectURL(url);
}
// Usage
downloadFile(JSON.stringify(data, null, 2), 'export.json', 'application/json');
```
## API Integration
### CORS-Enabled APIs (call directly from browser)
These APIs allow direct browser requests without a proxy:
| API | Use Case |
|-----|----------|
| GitHub API | Repos, gists, issues |
| PyPI | Package metadata |
| iNaturalist | Species observations |
| Bluesky | Social data |
| Mastodon | Fediverse data |
| OpenAI/Anthropic | LLM calls (with user's key) |
### LLM API Pattern
Call LLM APIs directly using localStorage-stored keys.
```javascript
async function callClaude(prompt) {
const key = localStorage.getItem('anthropic_api_key');
if (!key) throw new Error('No API key');
const response = await fetch('https://api.anthropic.com/v1/messages', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
'x-api-key': key,
'anthropic-version': '2023-06-01',
'anthropic-dangerous-direct-browser-access': 'true'
},
body: JSON.stringify({
model: 'claude-sonnet-4-20250514',
max_tokens: 1024,
messages: [{ role: 'user', content: prompt }]
})
});
return response.json();
}
```
### GitHub Gists for Persistent State
Use gists as a free, CORS-friendly database.
```javascript
async function saveToGist(gistId, filename, content, token) {
await fetch(`https://api.github.com/gists/${gistId}`, {
method: 'PATCH',
headers: {
'Authorization': `token ${token}`,
'Content-Type': 'application/json'
},
body: JSON.stringify({
files: { [filename]: { content: JSON.stringify(content) } }
})
});
}
```
## Advanced Capabilities
### Pyodide (Python in Browser)
Run Python, pandas, matplotlib entirely client-side.
```html
<script src="https://cdn.jsdelivr.net/pyodide/v0.24.1/full/pyodide.js"></script>
<script>
async function runPython(code) {
const pyodide = await loadPyodide();
await pyodide.loadPackage(['pandas', 'matplotlib']);
return pyodide.runPython(code);
}
</script>
```
### WebAssembly Libraries
Run compiled libraries in browser:
- **Tesseract.js** — OCR for images and PDFs
- **FFmpeg.wasm** — Video/audio processing
- **sql.js** — SQLite in browser
```html
<!-- Tesseract OCR example -->
<script src="https://cdn.jsdelivr.net/npm/tesseract.js@4/dist/tesseract.min.js"></script>
<script>
async function ocr(imageUrl) {
const result = await Tesseract.recognize(imageUrl, 'eng');
return result.data.text;
}
</script>
```
## Output Format
When building an HTML tool, deliver:
```
HTML TOOL: [name]
[Complete single-file HTML with inline CSS and JavaScript]
FEATURES:
- [Key capability 1]
- [Key capability 2]
USAGE:
[Brief instructions]
HOST: Save as .html and open in browser, or deploy to GitHub Pages.
```
## Reference
[Useful patterns for building HTML tools](https://simonwillison.net/2025/Dec/10/html-tools/) — Simon WillisonQuick Install
$
npx ai-builder add skill rohunvora/html-toolsDetails
- Type
- skill
- Author
- rohunvora
- Slug
- rohunvora/html-tools
- Created
- 4d ago