{"id":51674429,"url":"https://github.com/fabiobiondi/spinner-games","last_synced_at":"2026-07-15T03:35:31.090Z","repository":{"id":362162499,"uuid":"1257606653","full_name":"fabiobiondi/spinner-games","owner":"fabiobiondi","description":"Spinner Games for your website for Angular, React, Vue and Web Components","archived":false,"fork":false,"pushed_at":"2026-06-18T22:01:09.000Z","size":330,"stargazers_count":6,"open_issues_count":0,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-06-18T23:22:28.014Z","etag":null,"topics":["angular","react","vue","webcomponent"],"latest_commit_sha":null,"homepage":"https://spinner-games.vercel.app/","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/fabiobiondi.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":null,"dco":null,"cla":null}},"created_at":"2026-06-02T20:54:18.000Z","updated_at":"2026-06-18T22:01:20.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/fabiobiondi/spinner-games","commit_stats":null,"previous_names":["fabiobiondi/spinner-games"],"tags_count":2,"template":false,"template_full_name":null,"purl":"pkg:github/fabiobiondi/spinner-games","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabiobiondi%2Fspinner-games","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabiobiondi%2Fspinner-games/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabiobiondi%2Fspinner-games/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabiobiondi%2Fspinner-games/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/fabiobiondi","download_url":"https://codeload.github.com/fabiobiondi/spinner-games/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/fabiobiondi%2Fspinner-games/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":35489693,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-26T15:22:16.424Z","status":"online","status_checked_at":"2026-07-15T02:00:06.706Z","response_time":131,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["angular","react","vue","webcomponent"],"created_at":"2026-07-15T03:35:30.294Z","updated_at":"2026-07-15T03:35:31.078Z","avatar_url":"https://github.com/fabiobiondi.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# spinner-games\n\n[![npm version](https://img.shields.io/npm/v/spinner-games.svg)](https://www.npmjs.com/package/spinner-games)\n[![npm downloads](https://img.shields.io/npm/dm/spinner-games.svg)](https://www.npmjs.com/package/spinner-games)\n[![license](https://img.shields.io/npm/l/spinner-games.svg)](https://github.com/fabiobiondi/spinner-games/blob/main/LICENSE)\n\n![spinner-games preview — Pong, Breakout, Bubbles and Flappy mini-games](https://raw.githubusercontent.com/fabiobiondi/spinner-games/main/docs/screenshot.png)\n\nTiny, playable **web-component games** you can drop in as loaders/spinners\ninstead of the classic spinning circle. Built with [Lit](https://lit.dev) +\nTypeScript + Vite, shippable as a standalone library.\n\n## Status\n\nPlayable mini-games (`\u003cspinner-pong\u003e`, `\u003cspinner-breakout\u003e`,\n`\u003cspinner-bubbles\u003e`, `\u003cspinner-flappy\u003e`, `\u003cspinner-plinko\u003e`) are in place, usable\nas interactive loaders or self-running demos.\n                |\n\n# USAGE\n\nInstall the package:\n\n```bash\nnpm install spinner-games\n```\n\nThe games are framework-agnostic custom elements. Import the package once to\nregister the tags, then for Angular, React, Vue and Web Components:\n\n```js\nimport 'spinner-games'\n```\n\n```html\n\u003cspinner-pong\u003e\u003c/spinner-pong\u003e\n```\n\n## Documentation\n\nPer-environment install \u0026 usage guides live in [`docs/`](docs/):\n\n- [Web components (no framework)](docs/web-components.md) — plain HTML, bundlers, CDN\n- [React](docs/react.md) — `spinner-games/react`\n- [Vue](docs/vue.md) — `spinner-games/vue`\n- [Angular](docs/angular.md) — `spinner-games/angular`\n\n## Framework wrappers\n\nThe same components ship with thin, typed wrappers for React, Vue, and Angular\nvia subpath exports — no `customElements` boilerplate, full prop type-checking.\nEach subpath registers the underlying custom elements for you. Install the\npackage alongside your framework (declared as optional peer dependencies):\n\n```bash\nnpm install spinner-games\n```\n\n### React (`spinner-games/react`)\n\nBuilt on [`@lit/react`](https://www.npmjs.com/package/@lit/react), so props map\nto the element's reactive properties (not stringified attributes) and `ref`\npoints at the element instance.\n\n```tsx\nimport { Pong, Flappy } from 'spinner-games/react'\n\nexport function Loader() {\n  return (\n    \u003c\u003e\n      \u003cPong autoplay difficulty={0.6} /\u003e\n      \u003cFlappy obstacles=\"cave\" /\u003e\n    \u003c/\u003e\n  )\n}\n```\n\n### Vue (`spinner-games/vue`)\n\nTyped `defineComponent` wrappers — use them straight from a SFC:\n\n```vue\n\u003cscript setup lang=\"ts\"\u003e\nimport { Bubbles } from 'spinner-games/vue'\n\u003c/script\u003e\n\n\u003ctemplate\u003e\n  \u003cBubbles autoplay :show-new-line=\"30\" /\u003e\n\u003c/template\u003e\n```\n\nPrefer the raw tags instead? Tell Vue they're custom elements and the bundled\n`GlobalComponents` types will type them in templates:\n\n```ts\n// vite.config.ts\nvue({ template: { compilerOptions: { isCustomElement: (t) =\u003e t.startsWith('spinner-') } } })\n```\n\n### Angular (`spinner-games/angular`)\n\nStandalone **directives** matching each tag — import the one(s) you need (or the\n`SPINNER_GAMES_DIRECTIVES` bundle). The directive makes the tag a known element\n(no `CUSTOM_ELEMENTS_SCHEMA` needed) and gives its inputs full type-checking,\nwhile rendering the real custom element with no extra DOM node:\n\n```ts\nimport { Component } from '@angular/core'\nimport { SpinnerPong } from 'spinner-games/angular'\n\n@Component({\n  standalone: true,\n  imports: [SpinnerPong],\n  template: `\u003cspinner-pong autoplay [difficulty]=\"0.6\"\u003e\u003c/spinner-pong\u003e`,\n})\nexport class LoaderComponent {}\n```\n\n## Shared conventions\n\nEvery game is responsive (fills its parent, height from a\n`--spinner-*-aspect` ratio) and shares the `--spinner-color` token. These\nopt-in attributes are common to the playable games (`\u003cspinner-pong\u003e`,\n`\u003cspinner-breakout\u003e`, `\u003cspinner-bubbles\u003e`, `\u003cspinner-flappy\u003e`,\n`\u003cspinner-plinko\u003e`):\n\n| Attribute              | Default | What it does                                                                 |\n| ---------------------- | ------- | ---------------------------------------------------------------------------- |\n| `autoplay`             | off     | **Demo mode**: the game plays itself forever and user input is disabled.      |\n| `show-background`      | **on**  | Paint the panel background behind the game (a faint `--spinner-color` tint, overridable via `--spinner-bg`). Set it off to drop the background so the game floats transparently over whatever is behind it. |\n| `track-outside`        | off     | Keep following the mouse even after the pointer leaves the canvas (pointer-steered games only — `\u003cspinner-flappy\u003e` flaps and ignores it). |\n| `allowOutsideControls` | off     | Extend the game's pointer controls past the canvas edge — a superset of `track-outside`. The mouse keeps steering off the element **and** a click outside still acts: it fires a shot in `\u003cspinner-bubbles\u003e` and flaps in `\u003cspinner-flappy\u003e` (the only game where it adds a control without `track-outside`). `\u003cspinner-pong\u003e`/`\u003cspinner-breakout\u003e` have no in-game click, so for them it just extends steering. |\n\n```html\n\u003c!-- a real game you play --\u003e\n\u003cspinner-breakout\u003e\u003c/spinner-breakout\u003e\n\n\u003c!-- a self-running demo/loader (not interactive) --\u003e\n\u003cspinner-breakout autoplay\u003e\u003c/spinner-breakout\u003e\n\u003cspinner-pong autoplay\u003e\u003c/spinner-pong\u003e\n```\n\n### Color\n\nColor isn't an attribute or prop — every game reads the `--spinner-color` CSS\ncustom property. Set it **inline**, in a **stylesheet**, or on any **ancestor**\n(it cascades), and it works identically in HTML, React, Vue and Angular:\n\n```html\n\u003c!-- inline, on a single instance --\u003e\n\u003cspinner-pong autoplay style=\"--spinner-color: #4ade80\"\u003e\u003c/spinner-pong\u003e\n```\n\n```css\n/* in a stylesheet — recolors every game on the page */\nspinner-pong {\n  --spinner-color: #4ade80; /* green */\n}\n\n/* …or set it once on a parent and it cascades into every game inside */\n.loaders {\n  --spinner-color: #e879f9;\n}\n```\n\nThe panel background is a faint tint of this color; override it on its own with\n`--spinner-bg`. See each framework's guide in [`docs/`](docs/) for idiomatic\nsnippets (e.g. React needs a `React.CSSProperties` cast for the inline form).\n\n`\u003cspinner-bubbles\u003e` also takes `showNewLine=\"\u003cseconds\u003e\"` — every that-many\nseconds a new row of bubbles drops in from the top and the stack slides down,\nramping up the difficulty (`0`, the default, disables it):\n\n```html\n\u003cspinner-bubbles showNewLine=\"30\"\u003e\u003c/spinner-bubbles\u003e\n```\n\n`\u003cspinner-flappy\u003e` takes `obstacles` to choose what the ball threads through —\n`walls` (the default: classic paired pipes with a gap) or `cave` (an irregular\nrocky ceiling and floor with a winding passage between them). It works in both\nthe playable and `autoplay` modes:\n\n```html\n\u003cspinner-flappy obstacles=\"cave\"\u003e\u003c/spinner-flappy\u003e\n\u003cspinner-flappy autoplay obstacles=\"cave\"\u003e\u003c/spinner-flappy\u003e\n```\n\n`\u003cspinner-plinko\u003e` is a Plinko / Galton board: a ball drops from the top, bounces\nthrough a staggered grid of pegs and lands in a scoring slot. The default 7-slot\nboard subtracts `1 · 10 · 50 · 100 · 50 · 10 · 1` (biggest in the center). You\nstart at `startScore` (default 500) and burn it down: aim with the mouse and\nclick (or press Space) to drop each ball, and reach 0 within your `balls`\n(default 10) to win — a hit fires a confetti burst; run out of balls first and\nthe run is over. Tune it with `rows` (peg rows), `slots` (scoring bins), `balls`\n(drops you get) and `startScore` (where the countdown begins):\n\n```html\n\u003cspinner-plinko rows=\"10\" slots=\"9\" balls=\"12\" start-score=\"800\"\u003e\u003c/spinner-plinko\u003e\n\u003cspinner-plinko autoplay\u003e\u003c/spinner-plinko\u003e\n```\n\n\n## Contributions\n\n```bash\nnpm install\nnpm run dev        # open the playground (Vite dev server)\n```\n\n## Scripts\n\n| Script                | What it does                                                |\n| --------------------- | ---------------------------------------------------------- |\n| `npm run dev`         | Dev server + playground (`index.html`)                     |\n| `npm run build`       | Build everything: core + React/Vue + Angular into `dist/`  |\n| `npm run build:lib`   | Build core web components + React/Vue wrappers (Vite + tsc) |\n| `npm run build:angular` | Build the Angular wrappers (ng-packagr, partial-Ivy)     |\n| `npm run preview`     | Preview the production build                                |\n| `npm run typecheck`   | Type-check only, no emit                    \n\n\n## Adding a game\n\nEach game is a self-registering custom element:\n\n1. Create `src/components/spinner-\u003cname\u003e.ts` extending `LitElement`.\n2. Register it with `@customElement('spinner-\u003cname\u003e')`.\n3. Re-export it from `src/index.ts`.\n\n## Project structure\n\n```\nindex.html                    # dev playground\nsrc/                          # the core web components (the published `spinner-games`)\n  index.ts                    # library entry — re-exports all components\n  components/\n    spinner-pong.ts           # self-playing Pong loader\n    spinner-breakout.ts       # self-playing Arkanoid/Breakout loader\n    spinner-bubbles.ts        # self-playing Puzzle Bobble / bubble-shooter loader\n    spinner-flappy.ts         # self-playing Flappy Bird loader (a ball for a bird)\n    spinner-plinko.ts         # self-playing Plinko / Galton board loader\nwrappers/                     # framework wrappers — each imports the core as `spinner-games`\n  react/index.ts              #   React wrappers (subpath: spinner-games/react)\n  vue/index.ts                #   Vue wrappers (subpath: spinner-games/vue)\n  angular/                    #   Angular wrappers — separate ng-packagr build\n    src/                      #     standalone directives + public-api\n    ng-package.json           #     (subpath: spinner-games/angular)\n    tsconfig.lib.json\nvite.config.ts                # core build (src → dist/index.*)\nvite.config.wrappers.ts       # React/Vue build (wrappers → dist/react, dist/vue)\n```\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffabiobiondi%2Fspinner-games","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Ffabiobiondi%2Fspinner-games","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Ffabiobiondi%2Fspinner-games/lists"}