NCAA180-Sleeper
League Data for NCAA 180
5v5 플래그 풋볼 화이트보드, 선수 배치·경로·커버리지 지정, 플레이 저장·PNG 내보내기
A 5v5 flag-football whiteboard. Drag players on the field, tap one to give them a route or a coverage, save plays and export a PNG.
스포츠·피트니스TypeScript · JavaScriptGitHub @andysolomon온라인운영자 미확인
써 보셨어요? 한 번만 눌러 주세요. 로그인은 필요 없어요.
Coming Soon
저장소 README에서 가져옴
README 전체를 불러오는 중입니다. 아래는 저장된 발췌입니다.
A 5v5 flag-football whiteboard. Drag players on a green field, tap one to give it a route or a run (offense) or a coverage (defense), save plays, and press ▶ to watch the play run: the centre snaps, the QB hands off or throws, and the marked primary read gets the ball 80% of the time (the other drawn receivers share the rest; exported clips always complete to the primary).README 전문 보기 ↗A 5v5 flag-football whiteboard. Drag players on a green field, tap one to give it a route or a run (offense) or a coverage (defense), save plays (Save in the header, or ⌘S / Ctrl+S), and press ▶ to watch the play run: the centre snaps, the QB hands off or throws, and the marked primary read gets the ball 80% of the time (the other drawn receivers share the rest; exported clips always complete to the primary).
Every drive starts on your own goal line: yards count down into their end zone, so that is the 40. Line of scrimmage in Play tools is your board's setting, not a play's: put the ball on the 20, the 10 or the 5 and every play you open or start is drawn from there, through New play, a switch of book and a reload, and the yard numbers, no-run zones, goal line and end zone move with it. A play is saved from the spot it is drawn at, so its pictures, printouts and links show the same field; a play saved from the 5 keeps that field in the gallery until you save it again from the board. Signed in, the spot follows you to every device with the rest of your look. See ADR 015.
The field shows as much of itself as your screen's shape gives, and more when the play needs it: up to 37 yards past the line of scrimmage on a tall phone, 16 on a laptop. Drawing a custom route opens the field that deep on any screen, so a 25-yard post or a fade to the back of the end zone lands where you tap, and a route drawn deep is never cut off, on the field or in its pictures. Deep field, on the field's top right corner in the designer, opens the field to the end line, the end zone whole (from the drive start that is 50 yards on, past what even a tall phone shows), for dragging a deep safety or a waypoint further; it is kept on the device (and in your account, signed in), and offered wherever it shows more than the field already does.
With the ball in a no-run zone (the 5), the route palette marks its runs with the band's hatching and says why. A run can still be drawn, but a play whose call is a run is flagged: a yellow RUN IN NO-RUN ZONE stamp in the backfield of the field and of every thumbnail, share snapshot and printout, "Run in a no-run zone" on its play card and in its playbook, and in the slides' alt text and speaker notes. You're told when a play becomes flagged, however that happens: a run picked, the ball moved onto the 5, the primary read moved onto the runner. A handoff or a lateral that ends in a throw (play-action, a double pass or a lateral pass) is a pass and is never flagged, and neither is a league that turns No-run zones off.
Lateral (the QB's RUN group) tosses the ball back to another red player, who can lateral again, to anyone (the QB again included), throw or keep it, as many times as the play needs: every lateral is caught behind the line and never ahead of where it was let go. Each is drawn as a dashed arc with a diamond at the catch to drag; a catch dragged forward snaps back and says why. The call reads Double pass, Lateral pass or Lateral run by how the chain ends, and wristbands say where the ball goes (QB › Z › QB › X › Y). A play saved with the old Pitch opens as the QB's lateral to that runner when they had the ball; a pitch beside a runner who had it stays a decoy. Tap a step of the palette's ball path to change that toss. On the run, beside Lateral, is the option: the carrier runs with the ball to a round handle (drag it; it stays behind the line) and pitches from there, so a speed option or a pitch sweep draws and plays as it is run. See ADR 004, ADR 005 and ADR 007.
Jet sweep (the RUN group) sends a receiver across the formation behind the line at full speed, and the QB hands the ball off as they pass: in flag football a forward pass can't go to a player behind the line, so a jet sweep is always a handoff. The sweep is laid out from where the runner lines up, into a lane just in front of a shotgun QB (or just behind one under centre), past the QB and on to the far sideline, so a runner on the left sweeps right. Press ▶ and the runner is already crossing when the ball is snapped; everyone else and the ball hold until the snap, and the handoff lands behind the line. It is offered only to a player who can take a handoff at the snap: not the QB, the C or a player carrying a lateral. It is a run, so it is flagged in a no-run zone, and beside pass routes it is play-action: the jet fake. See ADR 006.
Rollout and Boot (the quarterback's own QB group, first in their palette) move the launch point. On a rollout the QB takes the snap, runs across behind the line to the right (⇄ Mirror route sends it left) and throws on the run from the ring at the end of their path, 8 yards over. A boot is play-action the other way: the QB fakes the handoff to the runner (the read when it is a run, else whoever reaches them first), then pivots back, bends behind the spot and rolls out away from where the fake goes, so moving the runner or flipping the play turns it (the palette says when only ⇄ Mirror on the runner's route will, for a back lined up straight behind the QB). Both are passes and never flagged in a no-run zone; with the read on the runner the QB hands off and carries the boot out as a decoy, and the play is a run. Playback holds the QB at the spot for the snap and the fake, carries the ball with them and throws from the ring; the words say it too ("Play-action: fake to Z, QB boots left, then throws."). See ADR 011.
Drag (the passing tree, beside Cross) is the shallow crosser: two yards up, then across the formation just past the line, ending three yards deep, so it sits under a Cross or a Post for a mesh or a rub. Like every preset it runs toward the middle from a wide split, Mirror turns it toward the sideline, and the palette says when the sideline cuts it short.
Saved plays go into playbooks (/playbooks), which print without any server: wristband inserts (one per position on each play's own side, that route or coverage bold; defenders come tagged LC, LB, R, RC and S, as the offense comes tagged C, QB, X, Y and Z), binder pages (one detailed play per page, or four simple per page), two-sided postcards (two-up with cut lines, or one per 4 × 6 in sheet), a one-page flyer of six featured plays, a slide deck for team meetings (.pptx: a title slide, the plays at a glance, then one play per slide with every player's job, and your notes in the speaker notes) that opens in PowerPoint, Keynote and Google Slides, and a playbook file to hand to an assistant coach. The demo tour (/demo) covers a coach's week in ten short phone-shaped clips, each one focused enough to watch at the field, with what each clip says written under it. PDFs, slide decks and JSON files are written locally and work offline. Every slide is a picture drawn in Patrick Hand, so it looks the same in every app. Slide titles and speaker notes are real text in Arial, and so is anything you type on a slide you add. Creating a short playbook share link explicitly uploads that book as a snapshot; recipients can preview and import it without downloading a file. Individual plays export as .play.json files.
The designer works in one playbook at a time (the pick follows you between devices when you are signed in): the play name drops that book's plays down (numbered as the book is, the primary play marked, the play on the board checked) and the Playbooks tile drops every playbook down, so you switch books and plays without leaving the field. Switching to a book opens its primary play, the first play added until you make another one primary in the book; an empty book clears the board. See ADR 009.
Play art draws each play from its own side. A man defender whose receiver isn't in the picture wears an "on X" tag instead of an arrow, and a defensive call is stamped MAN, ZONE or MAN + ZONE, as a Madden play card is. Tick Offense in play art (or Defense in play art) in Play tools or the share dialog to draw the other team, faded, on that play's thumbnail, share snapshot and printouts.
The app follows the device's light or dark setting. Theme in Play tools (or playbook settings) pins Light or Dark on that device, and on every device signed in to your account. The dark theme is chalk on a dark board; the field, printed pages and exports stay ink on paper in both.
Roster in Play tools lays your positions out on the field with nothing drawn on them: tap a position (C, QB, X, Y, Z on offense; LC, LB, R, RC, S on defense, or any tag you give a player) and set who plays it in the right sidebar, starter first, with as much depth as you like; ▲ moves a player up, ✕ takes one off. The roster shows one side at a time: tap the Offense badge at the top to flip to the defense, or to both sides together. Back, at the top beside Play, takes you back to your play just as you left it (so does tapping Roster again). Drag a position (or step it with the arrow keys) to stand it where you like: it stays there on every lineup, never moves a play, and Reset spots puts it back home. Back on a play, Show player names (under Roster in Play tools, once a roster has a name) puts the starters of the lineup you pick under the tags, here and nowhere else: the roster is kept on this device with the team (and in a device backup), and never goes into a play, a share link or a play's printout. Save roster picture and Download roster sheet PDF, under EXPORT in the roster panel, are, beside a device backup, the only files with the names in them: a picture of the side on view for a phone, each position a card under its token with its starter on the highlighter and the backups in order, and a sheet to print, both sides' depth charts (with lines to pencil in where a position is open or has no backup), then every player against every position under the gaps to fill. See ADR 010. A tag renamed on a play is a fresh position. Every name has a page listing the positions they play and where they sit at each. Lineups are versions of the roster: the Starting Lineup is always there, and so is the Secondary Lineup, made from its depth charts (the next player up starts at each position, nobody starting twice on a side, and it follows every change you make to the Starting Lineup; branch it to change it by hand), the lineup's name field drops every lineup down to switch between, its fork button (Branch lineup) copies the one you are on (for a day a player is away, or a different opponent) to change on its own, the field follows the lineup you pick, and a deleted lineup falls back to the one it came from. See ADR 008.
Team in Play tools (or playbook settings) is your team's name and colour: the name goes on cards, slides and printed pages and is painted across your end zone; the colour paints the Home Team end zone, the touchdown banner and the exports' accents.
End zone in Play tools (or playbook settings) dresses the band past the goal line: Classic keeps the green, and each of the others (Home Team in your team's colour, Synthwave '84, Sakura, Matrix, Great Wave, 8-Bit, Event Horizon, after themes from Omarchy's collection) has its own design, idle animation and touchdown celebration, and letters your team's name in its own hand rather than the words END ZONE, which the band already is. Three are open from the start; every touchdown pass thrown with ▶ by the play's own offense (caught in the end zone, or caught short and carried in; not out the back of it) sets off confetti in that end zone's colours and opens the next. The end zone comes into view with the ball near their goal (Line of scrimmage). It is kept on the device (and in your account, signed in, touchdowns included), and printed pages and exports keep the classic green.
The design system and functional prototype live in design/; the app is a faithful port of design/Flag Football Play Designer.dc.html.
Requires Bun 1.4+.
bun install
bun dev # http://localhost:3000
bun test # unit tests for lib/play
bun run build && bun run test:e2e # browser journeys against the production build (Playwright; see e2e/README.md)
bun run lint # eslint (typescript-eslint strict)
bun run typecheck
bun run build
bun run icons # re-encode design/assets/icons → public/icons (already committed)
Live at https://arc-play-flag.vercel.app. Hosted on Vercel: every push to main deploys production; pull requests get preview URLs. Plays live in localStorage and, for a coach signed in with Google or X, in their account too: Clerk signs them in (NEXT_PUBLIC_CLERK_PUBLISHABLE_KEY/CLERK_SECRET_KEY), and an account holds any number of teams, each with its own plays, playbooks, settings, lineups and desk (the play on the board, the playbook picked, the reader's place, share-link revoke controls), plus how the app looks to the coach (theme, field, end zone, Deep field, the ball's spot, touchdowns), kept in the same Redis store as the share links, so a coach picks up exactly where they left off on any device. See accounts setup, ADR 012 and ADR 015. An organization owns teams its coaches share: its members, invites, teams, plays and playbooks live in Postgres (DATABASE_URL; bun run db:migrate applies db/migrations), while a coach's own teams stay in Redis as before. Every member sees every team the organization lets them, head coaches and assistants draw on theirs, a viewer looks and prints, and coaches on one team see each other's avatars and the play each is on. See organizations setup and ADR 013. Short-link sharing uses a server-side Upstash Redis store (UPSTASH_REDIS_REST_URL/UPSTASH_REDIS_REST_TOKEN, or the KV_REST_API_URL/KV_REST_API_TOKEN pair a Vercel Marketplace store injects); local editing and file transfers work without them. See sharing setup and retention and the architecture decision.
Manual deploy from a machine with the Vercel CLI:
bunx vercel --prod
app/ — Next.js App Router: the designer (/), the playbooks screen (/playbooks, one static shell that also shows a single book via ?book=<id> so it opens offline), the video tour (/demo), shared plays (/p/[id]), short playbook links (/s/[token]), sharing API (/api/shares), root layout, globals.css with the design tokens in @theme.components/ — Header, PlaySidebar, RouteSidebar, RosterSidebar (the depth chart per position, ADR 008), Field, PlayerToken, RouteLayer, Hint, PlayThumb; components/playbooks/ holds the playbook list, book editor and export panel, while components/demo/ defines the tour chapters and player cards.lib/play/ — pure domain code (routes, geometry, zones, history, storage, library, the derived call) with bun test coverage, plus the man tags and coverage stamp (marks.ts, coverage.ts), which the man-coverage-art journey covers, and the roster and its lineups (roster.ts, lineup.ts, ffpd.lineups.v1), which the roster journey covers. Plays are keyed by a generated id (ffpd.plays.v2); the prototype's name-keyed ffpd.plays.v1 is migrated on first read.lib/org/ — organizations (ADR 013): on the server, the store of organizations, memberships and invites (store.ts), their teams with plays and playbooks as rows of their own (teams.ts), who may do what (access.ts), presence over Redis (presence.ts); on the device, the engine that pushes each change with the time it was made, polls for what changed and who is here, and gates a viewer's writes (sync.ts, with lib/account/state.ts shared with the account engine). lib/db/sql.ts is the Postgres connection and db/migrations/ the schema; the routes are app/api/orgs/; the screens are components/account/OrgPanel.tsx (the ORGANIZATION section and the members dialog), Presence.tsx (avatars), and the organization's teams in components/playbooks/SendToTeam.tsx.e2e/ — Playwright browser journeys (*.journey.ts) that drive the production build through the saving, history, playbook, import, sharing and export flows with fictional fixtures; they run on every pull request.lib/endzone.ts — the end zones, how many touchdown passes open each, and the device's pick and count (, ); finds the moment a caught pass crosses the goal line. Each design is (its keyframes in the End zones section of ), and is the confetti.See DEMO_WORKFLOW.md to record, encode, extend, and verify the tour reproducibly.
ffpd.endzone.v1ffpd.touchdowns.v1lib/play/touchdown.tscomponents/endzone/art/<id>.tsxapp/globals.csscomponents/endzone/Celebration.tsxlib/theme.ts — the Auto / Light / Dark choice (ffpd.theme.v1) and the inline script the root layout runs before first paint, so a dark device never sees a flash of paper. The dark palette itself is the data-theme="dark" block in app/globals.css.lib/render/ — the play as static SVG markup, drawn from the same geometry as the live field (thumbnails, cards, printed pages).lib/export/ — page composition in points, the zero-dependency PDF writer and .pptx writer (pptx.ts, with its stored ZIP packer zip.ts), the rasteriser, and the wristband, binder, card, postcard, flyer, slides and playbook-file formats. Export code is loaded on demand.public/icons/ — the sticker PNGs, pre-optimised (≤ 8 KB each): ink on paper as <name>.png and the dark board's chalk version as <name>-dark.png, both in the page with the theme picking one (components/Sticker.tsx). Sources live in design/assets/icons and icons-dark; bun run icons re-encodes them. Dark sheets from the owner are cut by scripts/cut-stickers.ts, and the stickers with no sheet (new, notes, playbook, run routes) are drawn by scripts/tool-stickers.ts and scripts/run-stickers.ts (run with node).public/demos/ — lazy-loaded 576×720 (4:5, the app's phone layout under a caption band) tour clips: WebM first, MP4 fallback, and a WebP poster per chapter. The complete set is kept under 2 MB; components/demo/recorded-from.json records the UI it was recorded from, and bun test fails when that UI changes without a new recording.