diff --git a/.beans/vibed-59dh--bit-by-bit-hidden-attribute-defeated-by-css-displa.md b/.beans/vibed-59dh--bit-by-bit-hidden-attribute-defeated-by-css-displa.md new file mode 100644 index 0000000..8fea5dc --- /dev/null +++ b/.beans/vibed-59dh--bit-by-bit-hidden-attribute-defeated-by-css-displa.md @@ -0,0 +1,29 @@ +--- +# vibed-59dh +title: 'bit-by-bit: [hidden] attribute defeated by CSS display rules — celebration overlay stuck onscreen' +status: completed +type: bug +priority: normal +created_at: 2026-07-14T04:30:04Z +updated_at: 2026-07-14T04:32:45Z +--- + +User report: after onboarding, a bare "You did it!" celebration modal covers the app and its buttons appear dead. + +Root cause: `.celebration-overlay`, `.onboarding-overlay` set `display:flex` (and `#artworkActive` has inline `display:flex`, `.upload-preview` sets `display:flex`). Author CSS beats the UA stylesheet's `[hidden]{display:none}`, so toggling the `hidden` attribute in JS has no visual effect on these elements. The celebration overlay was therefore visible from page load with placeholder text, and hideCelebration() couldn't hide it. jsdom tests missed it (no layout). + +Fix: global `[hidden]{ display:none !important; }` rule (also covers the inline-style case on #artworkActive). + +- [x] Apply global [hidden] rule +- [x] Verify in a real headless browser: celebration hidden on load, onboarding visible on first run, hides after finishing + +## Summary of Changes + +Added `[hidden]{ display:none !important; }` to the base styles in `bit-by-bit/index.html` (with a comment explaining why). This makes the `hidden` attribute authoritative over author display rules (`.celebration-overlay`/`.onboarding-overlay` flex, `#artworkActive` inline flex, `.upload-preview` flex). + +Verified with headless Firefox screenshots (fresh profile each): +- Fresh load: onboarding wizard visible, no stray celebration modal. +- Seeded mid-progress state: main app renders, 3/4 pixels · 75%, overlays hidden. +- Final check-off: image completes to 4/4 · 100%, canvas keeps showing the finished art via the pendingCelebration snapshot, gallery count increments — confirming the celebration flow renders correctly in a real browser. + +Lesson recorded: jsdom flow tests check the hidden property, not computed style — visibility bugs need a real-layout browser check. diff --git a/.beans/vibed-nz22--bit-by-bit-live-favicon-mirrors-the-partially-reve.md b/.beans/vibed-nz22--bit-by-bit-live-favicon-mirrors-the-partially-reve.md new file mode 100644 index 0000000..0a67950 --- /dev/null +++ b/.beans/vibed-nz22--bit-by-bit-live-favicon-mirrors-the-partially-reve.md @@ -0,0 +1,28 @@ +--- +# vibed-nz22 +title: 'Bit-by-bit: live favicon mirrors the partially revealed artwork' +status: completed +type: feature +priority: normal +created_at: 2026-07-14T20:46:47Z +updated_at: 2026-07-14T20:53:40Z +--- + +Render the current artwork (revealed pixels solid, unrevealed as loading-static) into a 64x64 offscreen canvas and set it as the page favicon via a data URL, updating on reveal and theme change. Stays single-file, no external requests. + +- [x] Add placeholder in head +- [x] Add updateFavicon() offscreen renderer +- [x] Hook into renderArt() and applyTheme() +- [x] Verify no external requests / console errors + +## Summary of Changes + +- `index.html` head: `` + `` seeded with an inert 1px data URL. +- New `updateFavicon()` / `drawFavicon()`: renders the displayed image into a 64x64 offscreen canvas (integer cell size, letterboxed/centred), revealed pixels solid, unrevealed as an alternating static checker, transparent pixels left as canvas background. Falls back to an 8x8 checker placeholder when no artwork is active. Handed to both link tags via `toDataURL()`. +- Coalesced with a rAF flag so the burst of `renderArt()` calls from one check-off repaints the icon once; href only reassigned when the PNG actually differs. +- Hooked into `renderArt()` (covers reveal, pop, celebration snapshot, empty state) and `applyTheme()` (bg/static tones are theme-dependent). +- `docs/DESIGN.md` section 5 documents the behavior. + +## Verification + +Headless browser automation was unavailable in the sandbox, so `drawFavicon` and the reveal-order helpers were extracted verbatim from `index.html` and executed in Node against a recording canvas stub with a content-hashed `toDataURL`. With a 16x16 sprite at 5 checks x 8 px/check: 40/40 revealed cells painted solid, 116/116 unrevealed painted static, 100 transparent left as background, 0 mismatches; grid fills the full 64x64; href is stable across an unchanged redraw and changes when more pixels reveal. `grep` confirms no `http://`, `https://`, or `fetch(` in the file. diff --git a/.beans/vibed-orml--bit-by-bit-rename-app-to-paint-by-habits-design-cl.md b/.beans/vibed-orml--bit-by-bit-rename-app-to-paint-by-habits-design-cl.md new file mode 100644 index 0000000..fcbb9da --- /dev/null +++ b/.beans/vibed-orml--bit-by-bit-rename-app-to-paint-by-habits-design-cl.md @@ -0,0 +1,27 @@ +--- +# vibed-orml +title: 'bit-by-bit: rename app to ''Paint by Habits'' + design cleanup pass' +status: completed +type: task +priority: normal +created_at: 2026-07-14T18:53:51Z +updated_at: 2026-07-14T19:06:14Z +--- + +User requests: +1. Rename the app from "Bit by Bit" to "Paint by Habits" (in-app strings, docs). Keep the localStorage key (bitbybit.v1) for backward compatibility with existing user data. +2. Capture populated screenshots of every view at desktop and mobile sizes (seeding state artificially where needed), then run a design-focused agent pass to clean up the visual design. + +- [x] Rename app in index.html (title, header, about, export filename, aria text; storage key kept for compat) +- [x] Rename in README/docs +- [x] Screenshot harness: seeded states, 9 views x mobile/desktop incl. celebration modal (sync-timer trick) +- [x] Design agent pass applied (Fable fork; 18 before/after screenshots) +- [x] Re-screenshot + re-validate (node --check clean, zero external refs, after-shots verified) + +## Summary of Changes + +1. **Rename:** app is now "Paint by Habits" — page title, header, About, storage-warning/import/upload error copy, export filename (`paint-by-habits-backup-*.json`), and all project docs. localStorage keys (`bitbybit.v1`) intentionally unchanged for backward compatibility with existing user data. +2. **Screenshot harness** (scratchpad `design-shots/`): awk-injected config/seed/driver scripts produce 9 seeded views (main light+dark, onboarding 1+2, four settings tabs, celebration modal via synchronous-timer shim) at 390×844 and 1440×900 in headless Firefox. +3. **Design pass (Fable fork):** two-column desktop layout ≥880px with sticky artwork card; fixed onboarding overflow at 390px; nowrap buttons + visible disabled states; settings tabs no longer clip on mobile; styled `::file-selector-button`; dark-mode progress track visible; clamp()-sized tinted header title; preset label/spacing polish. Verified across all 18 after-shots; `node --check` clean; still zero external references (73.5KB). + +Related: `vibed-p2es` (arbitrary emoji input) created as todo, not implemented per user instruction. diff --git a/.beans/vibed-p2es--bit-by-bit-allow-arbitrary-emoji-for-habits-not-ju.md b/.beans/vibed-p2es--bit-by-bit-allow-arbitrary-emoji-for-habits-not-ju.md new file mode 100644 index 0000000..4d8bbde --- /dev/null +++ b/.beans/vibed-p2es--bit-by-bit-allow-arbitrary-emoji-for-habits-not-ju.md @@ -0,0 +1,26 @@ +--- +# vibed-p2es +title: 'bit-by-bit: allow arbitrary emoji for habits, not just the curated list' +status: todo +type: feature +created_at: 2026-07-14T18:53:47Z +updated_at: 2026-07-14T18:53:47Z +--- + +User request: habits should accept any emoji, not only the 24 curated options in the picker. + +Current behavior: the emoji picker is a native with a small free-text input (maxlength ~4 to allow multi-codepoint emoji like 👨‍🌾; validate with Intl.Segmenter or a grapheme-aware check, falling back to length caps). +- Sanitize: trim, take the first grapheme cluster; empty → auto-assign as today. +- Keep keyboard accessibility and the 🎲 Auto option. +- Applies to both the onboarding wizard and Settings → Habits (add + rename flows). +- Export/import already stores emoji as plain strings, so no data model change expected. + +- [ ] Free-form emoji input in onboarding +- [ ] Free-form emoji input in Settings → Habits +- [ ] Grapheme-aware validation + auto fallback +- [ ] Verify in real browser (screenshots) diff --git a/.beans/vibed-vytp--bit-by-bit-pixel-art-habit-tracker-single-file-htm.md b/.beans/vibed-vytp--bit-by-bit-pixel-art-habit-tracker-single-file-htm.md new file mode 100644 index 0000000..c6bd5de --- /dev/null +++ b/.beans/vibed-vytp--bit-by-bit-pixel-art-habit-tracker-single-file-htm.md @@ -0,0 +1,41 @@ +--- +# vibed-vytp +title: Bit by Bit — pixel-art habit tracker (single-file HTML app) +status: completed +type: feature +priority: normal +created_at: 2026-07-14T00:18:14Z +updated_at: 2026-07-14T04:13:54Z +--- + +Build a local-only, single-page .html habit tracker where checking off daily habits reveals a pixel-art image one pixel at a time. + +Requirements: +- Daily habits list, user-defined +- Pixel art image revealed pixel-by-pixel per habit check-off +- Preset pixel art gallery + image upload (downsampled to grid) +- Dark/light mode (auto + manual toggle) +- localStorage only, zero server/network dependencies +- Export/import JSON backup for device migration +- Fun and whimsical, easy to use +- Deliverable: one self-contained index.html + +Process: Fable designs (docs/DESIGN.md), Sonnet agent implements, Opus agent reviews and iterates with Sonnet. + +- [x] Design spec written (DESIGN.md) +- [x] Project scaffolding (CLAUDE.md, README, docs/) +- [x] Sonnet implementation of index.html (1,496 lines, self-verified with jsdom flow tests) +- [x] Opus review round 1 + Sonnet fixes (NEEDS-FIXES → all 7 findings fixed, regression suite passing) +- [x] Opus verification pass (verdict: SHIP, all 7 fixes verified, no regressions) +- [x] Final validation (node --check clean, zero external references, 71.5KB single file) + +## Summary of Changes + +Built `bit-by-bit/`, a whimsical single-file pixel-art habit tracker. + +- **Design (Fable):** `docs/DESIGN.md` — full product spec: layout, reveal mechanics (seeded mulberry32 permutation, pixelsPerCheck sized for ~30-day completion), data model (`bitbybit.v1` localStorage doc), whimsy requirements, theming, export/import, a11y bar, 8 edge cases. +- **Implementation (Sonnet):** `index.html` (71.5KB, 1,576 lines) — vanilla JS/CSS IIFE, zero external requests, works from file://. 8 hand-shaded preset pixel artworks, image upload with pixelation preview, onboarding wizard, streaks + perfect-day sparkles, confetti celebration, dark/light/auto themes, JSON export/import, reduced-motion support. Verified with jsdom flow tests. +- **Review (Opus):** round 1 found 1 major (completion-moment canvas flash), 4 minors (seed-0 determinism, onboarding cap feedback, theme FOUC, hidden storage banner), 3 nits. All fixed by Sonnet; Opus verification pass returned SHIP with no regressions. Final cosmetic nit applied by Fable. +- **Scaffolding:** CLAUDE.md, README.md (with model credits), docs/PLANNING.md, docs/ARCHITECTURE.md. + +Not committed — awaiting user review. diff --git a/bit-by-bit/CLAUDE.md b/bit-by-bit/CLAUDE.md new file mode 100644 index 0000000..0ca5510 --- /dev/null +++ b/bit-by-bit/CLAUDE.md @@ -0,0 +1,19 @@ +@../CLAUDE.md + +# Paint by Habits + +A local-only, single-file HTML habit tracker that reveals a pixel-art image as +daily habits are checked off. + +## Project-specific notes + +- **This project is not a Rust crate.** The deliverable is a single + self-contained `index.html` (vanilla JS + CSS, no build step, no external + requests). +- The authoritative spec is `docs/DESIGN.md`. Behavior changes should update it. +- **Validation:** open `index.html` in a browser from `file://`; there must be + no console errors, no network requests, and the flows in DESIGN.md §9 (edge + cases) must hold. A quick static check: `grep` the file for `http://`, + `https://`, and `fetch(` — external references are forbidden (links in + comments/About text excepted). +- Deployment: copy `index.html` to any static host. No infra needed. diff --git a/bit-by-bit/README.md b/bit-by-bit/README.md new file mode 100644 index 0000000..d13ac12 --- /dev/null +++ b/bit-by-bit/README.md @@ -0,0 +1,53 @@ +# Paint by Habits 🎨 + +A whimsical habit tracker where consistency paints a picture. You define your +daily habits and pick a pixel-art image; every habit you check off reveals a few +pixels. Over days and weeks the artwork emerges — finish it, celebrate, pick the +next one. + +## What it is + +- **One self-contained file:** `index.html`. Vanilla JS + CSS, no build step, + no frameworks, zero network requests. Works from `file://` or any static host. +- **Local-only:** all data lives in your browser's `localStorage`. No server, + no accounts, no tracking. +- **Portable:** export your full state as a JSON backup and import it on + another device. + +## How it works + +Each artwork is a pixel grid. On start, a seeded PRNG (mulberry32 + +Fisher–Yates) fixes a random reveal order that stays stable across sessions. +Checking off a habit reveals `pixelsPerCheck` pixels — sized so a typical image +finishes in about 30 days of consistency. Pixels never un-reveal across days; +missing a day just pauses progress. Includes 8 built-in pixel artworks, or +upload any image to pixelate it into a 16–48 cell grid. + +Dark/light theme follows your OS by default, with a manual override. Streaks +count any day you checked at least one habit; days where you did everything get +a ✨ perfect-day sparkle. + +## Run it + +Open `index.html` in a browser. That's it. To host it, copy the file to any +static host. + +## Test it + +There is no test harness checked in (single-file app). Validation performed: +`node --check` on the extracted script, a grep proving zero external +references, and scripted jsdom flow tests covering onboarding, check/uncheck +persistence, day rollover, uncheck floor clamping, corrupt-state recovery, +storage-unavailable mode, import validation, and the completion/celebration +flow. See `docs/DESIGN.md` §9 for the edge-case checklist. + +## License + +Dual-licensed under [Apache-2.0](../LICENSE-APACHE) and [MIT](../LICENSE-MIT), +following Rust ecosystem convention for this repository. + +## Disclaimer + +This software was written with Claude Code. Design by **Claude Fable 5** +(`claude-fable-5`), implementation by **Claude Sonnet 5** (`claude-sonnet-5`), +review and verification by **Claude Opus 4.8** (`claude-opus-4-8`). diff --git a/bit-by-bit/docs/ARCHITECTURE.md b/bit-by-bit/docs/ARCHITECTURE.md new file mode 100644 index 0000000..0351da8 --- /dev/null +++ b/bit-by-bit/docs/ARCHITECTURE.md @@ -0,0 +1,18 @@ +# Paint by Habits — Architecture + +Everything lives in one file, `index.html`, organized into sections: + +| Component | Responsibility | +|---|---| +| **State** | Single JSON document under localStorage key `bitbybit.v1`; load/save with corruption guards; versioned for future migration. | +| **Mechanics** | Habit check/uncheck logging, daily rollover, streak math, seeded (mulberry32) reveal-order permutation, `pixelsPerCheck` and revealed-pixel derivation. | +| **Artwork** | Preset pixel-art definitions (palette + row strings); upload pipeline (offscreen canvas downsample → pixel grid); gallery of finished pieces. | +| **Rendering** | Canvas renderer for the artwork (revealed pixels, checkerboard for hidden, pop-in animation for deltas); DOM renderer for checklist, stats, settings. | +| **UI wiring** | Onboarding, settings dialog (habits / artwork / backup), theme cycling (auto→light→dark via `data-theme` + CSS custom properties), export/import, confetti celebration. | + +Data flows one way: user action → mutate state → persist → re-render. The +reveal permutation is derived from a stored seed, never persisted, so exports +stay small and rendering is deterministic. + +There is no server, no network I/O, and no build step. See `docs/DESIGN.md` for +the full specification. diff --git a/bit-by-bit/docs/DESIGN.md b/bit-by-bit/docs/DESIGN.md new file mode 100644 index 0000000..e8e660b --- /dev/null +++ b/bit-by-bit/docs/DESIGN.md @@ -0,0 +1,218 @@ +# Paint by Habits — Design Specification + +**Author:** Claude Fable 5 (design) · **Implementer:** Claude Sonnet · **Reviewer:** Claude Opus + +## 1. Product Concept + +Paint by Habits is a daily habit tracker where consistency paints a picture — literally. +The user defines daily habits and picks a pixel-art image. Every habit they check +off reveals a few pixels of the hidden artwork. Over days and weeks the image +emerges, and finishing it is the reward. Then they pick the next one. + +**Tone:** warm, playful, encouraging. Think "cozy pixel garden," not "productivity +dashboard." Copy should be friendly and lightly whimsical ("3 pixels bloomed!"), +never guilt-trippy. Missing a day never destroys progress — pixels, once revealed, +stay revealed. + +**Hard constraints:** +- One self-contained `index.html`. No external requests of any kind: no CDNs, no + fonts, no analytics, no fetch. Must work from `file://` and any static host. +- All state in `localStorage`. No server. +- Vanilla JS + CSS. No frameworks, no build step. +- Dark/light theme, auto-detected with manual override. +- Full-fidelity export/import so users can move devices. + +## 2. Layout (single screen, mobile-first) + +``` +┌──────────────────────────────┐ +│ ✿ Paint by Habits 🌓 ⚙️ │ header: title, theme toggle, settings +├──────────────────────────────┤ +│ │ +│ [ ARTWORK CANVAS ] │ the star of the show, centered, +│ progress: ▓▓▓░░ 42% │ progress bar + "142 / 340 pixels" +│ │ +├──────────────────────────────┤ +│ Today · Sunday, July 13 │ +│ 🔥 5-day streak │ +│ ┌──────────────────────┐ │ +│ │ 💧 Drink water [✓] │ │ today's habit checklist — +│ │ 🏃 Go for a run [ ] │ │ big, thumb-friendly rows +│ │ 📖 Read 10 pages [ ] │ │ +│ └──────────────────────┘ │ +├──────────────────────────────┤ +│ streak · total checks · │ small stat chips +│ images finished │ +└──────────────────────────────┘ +``` + +- Max content width ~28rem, centered; scales up gracefully on desktop. +- Settings (⚙️) opens a panel/modal with sections: **Habits**, **Artwork**, + **Backup** (export/import), **About**. Use `` or an equivalent overlay. +- First visit (no saved state): a friendly 2-step onboarding — (1) add habits, + (2) pick an artwork — then straight into the main screen. + +## 3. Core Mechanics + +### 3.1 Habits +- User-defined list, 1–12 habits. Each habit: name (≤40 chars) and an optional + emoji (offer a small emoji picker of ~24 curated options; default assigned + cyclically if skipped). +- Habits reset daily (local timezone). Checking a habit today logs it for today. +- Unchecking is allowed **same-day only** (undo mistaken taps). Past days are + immutable. +- Habits can be added/renamed/deleted in settings at any time. Deleting a habit + keeps its historical log entries (they still count toward revealed pixels). + +### 3.2 Pixel reveal +- The active image is a grid of pixels; each cell is either a color or + transparent. Only **opaque** pixels count. +- On image start, generate a random permutation of opaque pixel indices using a + **seeded PRNG (mulberry32)** with a stored seed. This is the reveal order — + stable across sessions and re-renders. +- `pixelsPerCheck` is computed **once, at image start**: + `max(1, round(opaqueCount / (habitCount * 30)))` — so a typical image finishes + in about a month of consistency. Locked for the life of the image even if + habits change (prevents gaming/confusion). +- `checks` counter on the image: +1 per habit check, −1 on same-day uncheck + (clamped ≥ 0 and never below checks from previous days — derive today's floor + from the log). Revealed pixel count = `min(opaqueCount, checks * pixelsPerCheck)`. +- Unrevealed cells render as a subtle dim checkerboard ("static"). Revealed + cells show their color and **pop in with a tiny scale/settle animation** when + newly revealed (animate only the delta, not on every render). +- When revealed == opaqueCount: **celebration** (see 5), image is archived to + the gallery, and the user is prompted to pick the next artwork. + +### 3.3 Artwork sources +1. **Presets:** 8 built-in pixel arts, each 16×16 to 24×24, defined compactly in + JS as `{name, palette, rows}` where rows are strings of palette keys + (`.` = transparent). Suggested set (implementer designs the actual art, and + should make it genuinely charming): mushroom, cat face, heart, rainbow, + rocket, sunflower, friendly ghost, coffee mug. +2. **Upload:** file input (image/*) → draw to offscreen canvas at a chosen grid + size (16 / 24 / 32 / 48) → sample each cell's center region; alpha < 128 → + transparent. Show a live preview + "~N days to finish" estimate before + confirming. Reject nothing — any image works; it just gets pixelated. +- Switching artwork mid-image warns that current progress will be archived as + unfinished (kept in gallery, marked incomplete) — or user cancels. + +### 3.4 Dates, streaks, stats +- "Today" = local date `YYYY-MM-DD` (from local time, not UTC). Recompute on an + interval (~30s) and on `visibilitychange` so a tab left open overnight rolls + over correctly (checklist resets, floor for uncheck updates). +- **Streak:** consecutive days (ending today or yesterday) where **all habits + then existing** were checked… simplify: a day counts if **every habit checked + that day ≥ 1** is too fuzzy — use: day counts if the user checked **all + currently-listed habits** is wrong for history. **Rule:** a day counts toward + the streak if **at least one habit was checked** that day; show a separate + "perfect day" sparkle ✨ on days where all habits were done. Streak of + any-activity is kinder and simpler; perfect days are the bonus flair. +- Stat chips: 🔥 current streak, ✅ total checks all-time, 🖼️ images finished. + +## 4. Data Model + +Single localStorage key: `bitbybit.v1`. Entire state is one JSON document: + +```jsonc +{ + "version": 1, + "theme": "auto", // "auto" | "light" | "dark" + "habits": [ + { "id": "h_x7f2", "name": "Drink water", "emoji": "💧" } + ], + "image": { // active artwork, null if none chosen + "name": "Cheeky Mushroom", + "source": "preset", // "preset" | "upload" + "w": 16, "h": 16, + "pixels": [null, "#e84d4d", ...], // row-major, length w*h, null = transparent + "seed": 123456789, + "pixelsPerCheck": 2, + "checks": 37, + "startedAt": "2026-07-01" + }, + "log": { // immutable history of check-offs + "2026-07-13": ["h_x7f2", "h_a3b9"] + }, + "gallery": [ // finished/archived artworks (cap 30, FIFO) + { "name": "Heart", "w": 16, "h": 16, "pixels": [...], + "finishedAt": "2026-06-20", "complete": true } + ] +} +``` + +- Save on every mutation (debounced is fine). Wrap `JSON.parse` of the loaded + value in try/catch; on corruption, keep the corrupt string under + `bitbybit.v1.corrupt` and start fresh rather than crashing. +- The reveal **order** is derived from `seed` at load time (don't store the full + permutation — keeps exports small; mulberry32 + Fisher–Yates is deterministic). +- Storage safety: 48×48 upload = 2304 pixels ≈ 20KB JSON worst case; gallery + capped at 30 entries. Well within quota. Still wrap `setItem` in try/catch and + surface a friendly error if it fails. + +## 5. Whimsy & Delight (required, not optional) + +- **Pixel pop:** newly revealed pixels scale/bounce in with slight random stagger. +- **Check-off feedback:** the habit row does a happy little squish; a toast like + "+2 pixels ✨" floats up near the canvas. +- **Completion:** self-contained canvas confetti burst (no library), the finished + image does a gentle celebratory wiggle, and a congratulations card appears + ("You painted *Cheeky Mushroom* — 24 days of showing up!") with a button to + choose the next artwork. +- **Copy:** playful microcopy throughout. Empty states are friendly ("No habits + yet — plant your first one 🌱"). +- **prefers-reduced-motion:** all animation (pop, confetti, wiggle) degrades to + simple fades or nothing. +- **Live favicon:** the browser-tab icon is a 64×64 miniature of the current + artwork in its current state — revealed pixels solid, unrevealed ones a + two-tone checker, transparent ones the canvas background — so the tab itself + fills in as habits are checked off. Drawn into an offscreen canvas and set as + a data-URL PNG on `` (and `apple-touch-icon`); no external + file, no network request. Redrawn on reveal and on theme change, coalesced to + one repaint per frame, and only reassigned when the image actually changes. + +## 6. Theming + +- CSS custom properties on `:root`; `data-theme="light"|"dark"` attribute set by + JS. `"auto"` follows `prefers-color-scheme` (and listens for changes). +- Toggle button cycles auto → light → dark (icon reflects state). +- Palette direction: soft, warm, slightly candy-colored. Dark mode is a cozy + near-black with muted accents, not pure #000. Both themes must keep the + artwork canvas neutral so the art's own colors read true. +- System font stack; headers get a chunky "pixel-ish" treatment via + letter-spacing/weight (no embedded fonts — keep the file lean). + +## 7. Export / Import + +- **Export:** button in Backup settings → downloads + `bit-by-bit-backup-YYYY-MM-DD.json` (pretty-printed full state) via Blob + + object URL. Also a "Copy to clipboard" fallback. +- **Import:** file picker (accept `.json,application/json`) → parse → validate + (`version === 1`, `habits` is array, sanity-check shapes) → show a summary + ("3 habits, 142 checks, 1 artwork in progress — replace current data?") → + on confirm, overwrite state and re-render. Invalid files get a kind error + message, never a crash. + +## 8. Accessibility & Quality Bar + +- Fully keyboard operable; visible focus states; habit checkboxes are real + `` or `role="checkbox"` with labels. +- Canvas has an `aria-label` describing progress; progress also exists as text. +- Color contrast ≥ 4.5:1 for text in both themes. +- Touch targets ≥ 44px. No horizontal page scroll at 320px width. +- No console errors. No globals leaking beyond one app namespace. +- Code organized in clear sections (state, rendering, mechanics, UI wiring) + with brief section comments — one file, but readable. + +## 9. Edge Cases (must handle) + +1. First run / empty state → onboarding. +2. Same-day uncheck cannot reduce `checks` below the floor established by + previous days' log entries. +3. Habit deleted after being checked today → today's log keeps the id; counts + still work. +4. Day rollover with tab open → checklist resets without reload. +5. Import of garbage / wrong-version / truncated JSON → friendly rejection. +6. localStorage full or unavailable (private mode) → app still renders; banner + warns that progress won't persist. +7. Image with very few opaque pixels (e.g., 20) finishes early — fine; celebrate. +8. `pixelsPerCheck` overshoot on final check just clamps to 100%. diff --git a/bit-by-bit/docs/PLANNING.md b/bit-by-bit/docs/PLANNING.md new file mode 100644 index 0000000..fbd2d5e --- /dev/null +++ b/bit-by-bit/docs/PLANNING.md @@ -0,0 +1,33 @@ +# Paint by Habits — Planning + +## Phases + +1. **Design** — product spec written by Claude Fable 5 (`docs/DESIGN.md`). ✅ +2. **Implementation** — single-file `index.html` implemented by Claude Sonnet + from the spec. +3. **Review & iterate** — Claude Opus reviews the implementation against the + spec; findings are fixed by Sonnet; Opus verifies. +4. **Ship** — file is validated standalone and handed off for static hosting. + +## Work log + +- **2026-07-13** — Project created (bean `vibed-vytp`). Design spec written. + Implementation and review pipeline run (Fable → Sonnet → Opus). +- **2026-07-13** — Sonnet delivered `index.html` (1,576 lines) with jsdom flow + tests. Opus review round 1: NEEDS-FIXES (1 major — completion-moment canvas + flash; 4 minor; 3 nits). Sonnet fixed all findings. Opus verification pass: + **SHIP**, no regressions. Final nit (render guards using `state.image` + instead of `displayImage()`) applied by Fable; `node --check` clean, zero + external references confirmed. v1 complete. +- **2026-07-13** — User-reported bug (bean `vibed-59dh`): celebration overlay + stuck onscreen from load, blocking the app. Root cause: author `display:flex` + rules override the UA's `[hidden]{display:none}`. Fixed with a global + `[hidden]{display:none !important}` rule; verified with headless Firefox + screenshots (fresh load, mid-progress, completion flow). +- **2026-07-14** — Renamed app to **Paint by Habits** (bean `vibed-orml`); + storage keys kept for backward compatibility. Built a headless-Firefox + screenshot harness (9 seeded views × mobile/desktop) and ran a design + cleanup pass (Claude Fable 5): two-column desktop layout, mobile overflow + and tab-clipping fixes, disabled-button affordances, styled file inputs, + dark-mode progress-track contrast, header typography. Follow-up bean + `vibed-p2es` (arbitrary emoji input) created, not yet implemented. diff --git a/bit-by-bit/index.html b/bit-by-bit/index.html new file mode 100644 index 0000000..d2669f6 --- /dev/null +++ b/bit-by-bit/index.html @@ -0,0 +1,1672 @@ + + + + + + +Paint by Habits — pixel habit tracker + + + + + + +
+
+

Paint by Habits

+
+ + +
+
+ + + +
+
+ + +
+
+ +
+
+ Today + 🔥 0-day streak +
+
    + +
    + +
    +
    🔥 0streak
    +
    0checks
    +
    🖼️ 0finished
    +
    +
    +
    + + + + + + +
    +

    Settings

    + +
    +
    + + + + +
    +
    +
    +
    Your habits
    +
    + +
    Add a habit
    +
    + + +
    + +
    + + + + + + +
    +
    + + + + + + + +