{
  "markdown": "# vue-pwa-starter\n\nA **local-first Vue 3 PWA starter** with the part most starters skip: a complete, tiered testing strategy and the mobile app-shell boilerplate you otherwise rebuild every time.\n\nEverything lives on the device (IndexedDB via Dexie). No backend, no accounts, no spinners. Built for apps you use on a phone, offline, mid-task.\n\n## Quickstart\n\nUse this repository as a GitHub template (or clone it), then:\n\n```bash\npnpm install\npnpm dev\n```\n\nAll gates should be green out of the box:\n\n```bash\npnpm check                                        # everything below that needs no browser, in parallel (~8 s)\npnpm test:storybook                              # component states, interactions, axe, themes + touch\npnpm test && pnpm test:a11y && pnpm test:visual   # integration + a11y + visual tiers\npnpm test:e2e                                     # production build in a real browser\npnpm test:mutation                                # mutation score over the unit tier (~10 s)\npnpm build && pnpm size-limit                     # bundle budget\n```\n\n`pnpm check` is the one to reach for while you work: it runs lint, formatting,\ntypes, dead-code, the unit tier and the architecture tier concurrently, keeps\ngoing after the first failure, and prints each task's output under its own\nlabel — so one run tells you everything that is broken.\n\n## What you get\n\n| Area             | What is in the box                                                                                                                                                                                                                                                                                                                                                                                                                                                                                      |\n| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |\n| App shell        | Config-driven bottom nav ([`OrganismAppShell.vue`](src/components/organisms/OrganismAppShell.vue)), optional center FAB slot, `meta.hideNav` escape hatch, safe-area insets, `TemplatePageLayout`/`MoleculePageHeader`, keyboard-aware bottom sheet (`MoleculeDialogContent`), toast viewport                                                                                                                                                                                                           |\n| UI components    | shadcn-vue-style primitives over [Reka UI](https://reka-ui.com/), copied rather than installed — compound parts, `cn()` class merging, `data-slot` targeting, `as-child`. The mobile inventory is there: list rows, a swipe-dismissible sheet, tabs, an action sheet, a chip row, a swipeable row, pull-to-refresh, empty states and alerts, plus a [swipeable numeric input](docs/numeric-input.md). The layer boundary is lint- and test-enforced. See [docs/ui-components.md](docs/ui-components.md) |\n| Design tokens    | Elevation, motion, layering, type and the shell's geometry are named tokens, not literals — with a dark-mode elevation model of their own, a Foundations story rendering each from the live custom property, and an architecture test that fails on a raw `shadow-lg`, `z-50` or `duration-200`. See [docs/design-tokens.md](docs/design-tokens.md)                                                                                                                                                     |\n| Local-first data | Dexie schema with a worked v1→v2 migration, converter pattern for reading old data forever, repository layer, zod-validated JSON export/import                                                                                                                                                                                                                                                                                                                                                          |\n| Example feature  | `src/features/notes` — one deliberately boring feature that touches every layer, with a test in every tier. Copy it, then delete it                                                                                                                                                                                                                                                                                                                                                                     |\n| Testing          | Eight tiers: unit, Storybook component contracts, browser integration, a11y, touch, visual, architecture, and production e2e. The catalogue opens at 390 px, carries a phone-frame and on-screen-keyboard toolbar aid, and holds written guidance beside the props tables. See [docs/testing-strategy.md](docs/testing-strategy.md) and [docs/design-system.md](docs/design-system.md)                                                                                                                  |\n| Test quality     | Stryker mutation testing scoped to the unit tier — grades whether the assertions would notice a bug, not whether the lines ran. Runs in ~10 s, own CI job. See [docs/mutation-testing.md](docs/mutation-testing.md)                                                                                                                                                                                                                                                                                     |\n| Quality gates    | oxlint + ESLint + Prettier + markdownlint, knip (dead exports), size-limit (bundle budget), husky pre-commit gate (~15 s)                                                                                                                                                                                                                                                                                                                                                                               |\n| PWA              | vite-plugin-pwa with update prompt, icons generated from one SVG at build time, offline precache, web-vitals seam                                                                                                                                                                                                                                                                                                                                                                                       |\n| Observability    | Every db operation is a named Effect span already; opt into OTLP export in development with one env var — no `@opentelemetry/*` dependency, nothing in the production bundle. See [Tracing in development](#tracing-in-development)                                                                                                                                                                                                                                                                     |\n| CI               | Sharded GitHub Actions pipeline, actions pinned by SHA, zizmor-clean                                                                                                                                                                                                                                                                                                                                                                                                                                    |\n| i18n             | vue-i18n with typed message keys, English + German                                                                                                                                                                                                                                                                                                                                                                                                                                                      |\n| Agent-ready      | No `CLAUDE.md`, no `AGENTS.md` — [`docs/`](docs/index.md) is an [Open Knowledge Format](https://github.com/GoogleCloudPlatform/knowledge-catalog/blob/main/okf/SPEC.md) bundle of concept files (including a full Effect v4 reference), and a `SessionStart` hook injects its index into every agent session. Humans and agents read the same file                                                                                                                                                      |\n\n## Stack\n\nVue 3.5 · TypeScript (strict) · Vite · Tailwind CSS v4 · reka-ui · VueUse · Dexie · vue-router · vue-i18n · zod · Vitest 4 (browser mode) · Playwright · pnpm (with catalogs — pnpm is required)\n\n## Philosophy: local-first\n\nDesign tie-breakers, borrowed from [Ink & Switch's local-first ideals](https://www.inkandswitch.com/local-first/):\n\n1. **No spinners** — instant input, never block on network\n2. **Network optional** — fully offline\n3. **The Long Now** — data readable after the app dies (export, schema stability, converters)\n4. **Ownership & control** — user owns the data, no accounts, exportable any time\n\nHow the data layer implements this: [docs/local-first.md](docs/local-first.md)\n\n## Project structure\n\n```text\nsrc/features/      Feature-owned UI, state, and domain logic (features never import features)\nsrc/db/            Dexie schema, converters, repositories — the only place that touches storage\nsrc/stores/        Shared app-wide state (@effect/atom-vue atoms, not Pinia)\nsrc/composables/   Shared reactive logic (2+ consumers)\nsrc/views/         Route-level pages; may compose multiple features\nsrc/components/    Shared components, tiered by atomic design — docs/atomic-design.md\n                   atoms/ molecules/ organisms/ templates/; every atom is a styled\n                   primitive (shadcn-style, yours to edit — docs/ui-components.md),\n                   as is a directory with a barrel; a flat .vue above atoms/ is a\n                   composite\nsrc/**/*.stories.ts Colocated isolated component states and interaction contracts\nsrc/stories/       Catalogue-only pages: Foundations (the tokens, rendered live),\n                   Guidelines (when to reach for what) and Patterns (composed\n                   recipes). Delete nothing here when you delete the example\n                   feature — none of it imports one\nsrc/__tests__/     Non-Storybook tests, mirroring the source tree\ntest/e2e/          playwright-bdd features + steps\n```\n\nThese boundaries are not just documentation — they are enforced by [architecture tests](src/__tests__/architecture/architecture.test.ts) over the module graph and by `no-restricted-imports` rules in [eslint.config.ts](eslint.config.ts), which also cover `.vue` files. [A negative test](src/__tests__/architecture/boundaries.test.ts) proves the enforcement actually fires.\n\n## Tracing in development\n\nThe instrumentation is already there: every repository operation is wrapped in `Effect.fn('NotesRepo.list')`, the backup programs add `Effect.withSpan`, and every reported failure emits a log record annotated with `boundary` / `operation` / `failure`. What is missing by default is somewhere to send it.\n\nStart a collector and point the app at it:\n\n```bash\ndocker run --rm -p 16686:16686 -p 4318:4318 jaegertracing/all-in-one\ncp .env.example .env.local\npnpm dev\n```\n\nOpen <http://localhost:16686>, pick the `vue-pwa-starter` service, and you get a span per db operation — including the `NotesRepo.create` → `NotesRepo.list` pair that shows a write invalidating `NOTES_KEY` and the read atom re-reading from disk.\n\nThree things make this cheap enough to ship in a starter:\n\n- The OTLP exporters live in `effect/unstable/observability` and post JSON over `fetch`, so there is no `@opentelemetry/*` SDK to install or bundle.\n- The dev server proxies `/_otlp` to `localhost:4318`, keeping the request same-origin — a stock collector rejects the CORS preflight an OTLP payload would otherwise trigger.\n- `import.meta.env.DEV` is a literal `false` in a production build, so the exporter is dead code. `pnpm size-limit` is what keeps that honest, and telemetry about a user's own notes never has the chance to leave their device.\n\n## Adding your first feature\n\nFollow the walkthrough in [docs/adding-a-feature.md](docs/adding-a-feature.md). Short version: copy how `src/features/notes` does it, tier by tier.\n\n## Deleting the example\n\nThe notes feature is scaffolding. To remove it:\n\n1. Delete `src/features/notes`, `src/views/NotesView.vue`, and the notes specs under `src/__tests__` and `test/e2e`.\n2. Remove the notes entries from `src/router/index.ts`, `src/router/navigation.ts`, and the `notes`/`quickAdd` keys from `src/i18n/messages/*`.\n3. Replace the `notes` table in `src/db/schema.ts` (and `repositories/`, `backup.ts`) with your own.\n4. Remove the `QuickAddNoteSheet` wiring from `src/App.vue` (keep the `#center-action` slot if you want a FAB).\n5. Run `pnpm check` — the gates will point at anything you missed.\n\n## License\n\n[MIT](LICENSE)\n",
  "bytes": 13560,
  "sha": "a2d681dc9d57965efd3c3d66cc77267f367435aca3d322854a50e8f5133277bb",
  "repo_slug": "alexanderop/vue-pwa-starter",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/okf_alexanderop_vue_pwa_starter_docs_index_m_89a5c39d/readme"
}