A daily CSS challenge game — like Wordle, but for CSS.
Each day, new challenges are generated across two modes:
- CSS Mode — Write CSS to match a target screenshot. You're given the HTML structure and starter stubs.
- Tailwind Mode — Add Tailwind utility classes to HTML elements to match the target. Editing is restricted to
classattribute values only.
Live at cssdaily.dev
- A new challenge is generated daily for each mode through a configurable OpenAI or Anthropic provider
- You see a target component and an editor — CSS editor with stubs, or HTML with empty class attributes
- Your score updates in real-time as you type, powered by pixel-level visual diffing
- Submit before the timer runs out to lock in your score
- Share your results with friends
Write CSS from scratch to match the target. Includes a split view toggle to see CSS and HTML side-by-side or in tabs.
Add Tailwind utility classes to pre-structured HTML. The editor locks everything except class="" values — non-editable regions are dimmed, editable regions are highlighted. Includes Tailwind utility autocomplete with Tab-to-accept.
- Astro — Static site generation
- React — Interactive challenge player
- CodeMirror 6 — CSS/HTML editor with transaction filters, decorations, and custom autocomplete
- Tailwind CSS — UI styling + Tailwind CDN for challenge rendering
- @zumer/snapdom — DOM-to-image capture for visual diffing
- Playwright — Target screenshot generation
- OpenAI API / Claude API — Configurable daily challenge generation
- GitHub Pages — Hosting
- GitHub Actions — CI/CD and daily challenge cron
The scoring system compares your rendered output against the target using pixel-level analysis:
- Both your output and the target are rendered in hidden iframes and captured with snapdom
- Pixel comparison uses Euclidean RGB distance with configurable tolerance
- Background pixels are auto-detected and excluded from scoring
- A power curve is applied to the raw score to reward precision over broad-stroke matches
For Tailwind challenges, the diff engine uses iframes with allow-scripts sandbox to load the Tailwind CDN, with class value sanitization for XSS defense-in-depth.
- A GitHub Actions cron job runs daily at 6am UTC
- The configured AI provider generates a new CSS challenge and a new Tailwind challenge (independently, with
continue-on-error) - Playwright renders the target to a screenshot PNG for each challenge
- The challenge JSONs and target images are committed to the repo
- The site automatically rebuilds and deploys to GitHub Pages
npm install
npm run devChallenge generation defaults to OpenAI with gpt-5.6-terra. Configure it in .env:
AI_PROVIDER=openai
AI_MODEL=gpt-5.6-terra
OPENAI_API_KEY=your_openai_api_key_hereTo use Anthropic without changing code, set AI_PROVIDER=anthropic, choose an Anthropic model in AI_MODEL, and provide ANTHROPIC_API_KEY. Only the selected provider is called; failures never fall back to the other provider.
Generate challenges locally with:
# CSS challenges
npx tsx scripts/generate-challenge.ts # generates for tomorrow
npx tsx scripts/generate-challenge.ts 2026-03-15 # generates for a specific date
# Tailwind challenges
npx tsx scripts/generate-tailwind-challenge.ts # generates for tomorrow
npx tsx scripts/generate-tailwind-challenge.ts 2026-03-15 # generates for a specific dateTo regenerate target PNGs from existing challenge JSONs:
npx tsx scripts/generate-targets.tsThe visual diff engine in this project is adapted from Synhax by the Syntax team. Huge thanks to Wes Bos, Scott Tolinski, and all contributors to Synhax for building and open-sourcing the pixel comparison approach that powers the scoring system here. Their work on Euclidean color distance, background detection, and gradient scoring was invaluable.
Camilo 💻 🚧 |
MIT