Skip to content

Repository files navigation

Counter Fresh Logo

Counter Fresh

Counter Fresh Demo Tour
High Resolution Preview

demo-tour-60fps.mp4

An animated multi-participant score tracker and tally counter.
Built with Fresh 2, Preact Signals, and Tailwind CSS v4, runnable on the Web and as a Desktop app via Deno.

Deno 2.x Fresh 2.3 Preact Signals Tailwind CSS v4 Vite 7 Web & Desktop


Features Features

  • Leaderboard Live Sorting: Add participants and update their scores. Cards automatically re-order by score (descending) with stable tie-breaking.

  • Sort Animated Re-ordering: Uses the FLIP (First, Last, Invert, Play) technique to smoothly slide cards into their new positions when scores change.

  • Delete Hold to Delete: Long-press the delete button (~850ms) to remove a participant. Displays a rising fill and shake animation, and cancels safely if released early.

  • Matrix Canvas Background: Animated HTML5 canvas background with falling glyphs, spotlight effects, and a blueprint grid pattern.

  • Desktop Web & Desktop Support: Run in the browser during web development or build as a desktop application using Deno Desktop (CEF backend).

  • Window Desktop Window Controls: Provides a window close button and Esc key shortcut wired to Deno Desktop bindings.

  • Design Tailwind CSS & Custom Theme: Styled using Tailwind CSS v4, custom OKLCH color palettes, and fonts (Righteous and JetBrains Mono).


Engineering Animation & Mechanics Implementation

Topology-Matched Path Morph Animation Demo

All animations across the project are implemented with native Web APIs and CSS, without external animation dependencies:

  • Vanilla FLIP Layouts (ParticipantList.tsx): Card re-ordering is animated using the FLIP (First, Last, Invert, Play) technique inside useLayoutEffect. Previous and next card bounding boxes are measured via getBoundingClientRect(), inverted using CSS transforms (translate(deltaX, deltaY)), and transitioned to their target positions on the next animation frame.
  • Topology-Matched Path Morphing (ParticipantIcon.css): The + add icon morphs into a user avatar on hover using CSS d: path(...) transitions. Both shapes share identical cubic Bézier segment topologies, enabling the browser to interpolate the path geometry without JavaScript.
  • Stateful Hold-to-Delete (Participant.tsx): The 850ms hold interaction runs on a requestAnimationFrame loop driven by performance.now(). Releasing early triggers a dynamic drain loop that rewinds progress back to 0 proportionally instead of resetting instantly.
  • Dual-Layer Canvas Background (GlyphsBackground.tsx): To avoid redundant per-frame redraws of static blueprint geometry, the background dot grid and frame guides are drawn once to an offscreen canvas. The active canvas renders falling glyphs and uses destination-out composite operations for radial spotlight masking.

Tech Stack Tech Stack

Layer Technology
Runtime & Toolchain Deno 2
Web Framework Fresh 2 (@fresh/core, @fresh/plugin-vite)
UI Library Preact + @preact/signals
Desktop Shell Deno Desktop (Chromium Embedded Framework backend)
Dev Server & Bundler Vite 7
Styling Tailwind CSS v4 + @tailwindcss/vite

Structure Project Structure

CounterFresh/
├── assets/                 # Stylesheets, theme configuration & showcase media
│   ├── demo/               # Documentation animation assets & previews
│   │   └── participant-morph.svg # Animated SVG demo preview
│   └── styles.css          # Tailwind CSS v4 setup, OKLCH tokens & keyframe animations

├── components/             # Reusable UI components & SVG icons
│   ├── CounterIcons/       # Plus and Minus SVG icons
│   ├── DeleteIcon/         # Animated delete icon
│   ├── ParticipantIcon/    # Add participant icon
│   ├── Button.tsx          # Base styled button
│   ├── HoldDeleteButton.tsx# Reusable hold-to-delete trigger
│   └── Surface.tsx         # Translucent card container
├── islands/                # Interactive Preact islands (client-hydrated)
│   ├── CloseButton.tsx     # Desktop window close trigger & keybinds
│   ├── Countdown.tsx       # Animated countdown island
│   ├── Counter.tsx         # Score increment/decrement island
│   ├── GlyphsBackground.tsx# Animated canvas background
│   ├── Participant.tsx     # Participant card with hold-to-delete logic
│   ├── Participant_test.ts # Colocated unit tests for hold-to-delete
│   ├── ParticipantList.tsx # FLIP-animated leaderboard list
│   └── ParticipantList_test.ts # Colocated unit tests for leaderboard sorting
├── routes/                 # File-system routing
│   ├── api/                # API route handlers
│   │   └── [name].tsx      # Sample API handler
│   ├── _app.tsx            # Global HTML document shell & background mounting
│   ├── about.tsx           # About page
│   └── index.tsx           # Home page
├── tests/                  # Integration & server endpoint test suites
│   └── api_test.ts         # API route & shutdown handler integration tests
├── static/                 # Static public assets (favicons, logos)
├── icons/                  # Application icons for desktop builds
├── bindings.d.ts           # Desktop native bindings TypeScript definitions
├── client.ts               # Client entry point for Vite HMR & assets
├── deno.json               # Deno configuration, tasks, desktop settings & imports
├── main.ts                 # Fresh 2 server entry point & desktop window lifecycle
└── vite.config.ts          # Vite plugin configuration

Getting Started Getting Started

Prerequisites

Make sure you have Deno 2.x installed:

# macOS / Linux
curl -fsSL https://deno.land/install.sh | sh

# Windows (PowerShell)
irm https://deno.land/install.ps1 | iex

Installation

Clone the repository and enter the directory:

git clone https://github.com/Tablerase/CounterFresh.git
cd CounterFresh
# Install dependencies (Deno will cache them automatically)
deno install

screenrecording-2026-08-25_16-12-31.mp4


Development Development

Web Development Mode

Starts the Vite development server with HMR:

deno task dev

Open http://localhost:5173 in your browser.

Desktop Development Mode

Launches the application in a desktop window with HMR:

deno task d-dev

Running Tests

Run the test suite across unit calculations, sorting logic, and API endpoints:

deno task test

Formatting & Type Checks

Run format check, linter, and typechecker:

deno task check

Build Building for Production

1. Web Production Build

Build client assets and start the production server:

# Build client assets
deno task build

# Start server
deno task start

2. Standalone Desktop Build

Package the application as a standalone desktop binary:

deno task d-build

Output directory (dist/):

  • macOS: dist/CounterFresh.app
  • Windows: dist/CounterFresh.exe
  • Linux: dist/counter-fresh

Settings Desktop Configuration

Desktop packaging options are configured under the "desktop" key in deno.json:

{
  "desktop": {
    "app": {
      "name": "Counter Fresh",
      "identifier": "com.example.myapp",
      "icons": {
        "macos": "./icons/app.png",
        "windows": "./icons/app.ico",
        "linux": "./icons/app.png"
      },
      "deepLinks": ["counter-fresh"]
    },
    "backend": "cef",
    "output": {
      "macos": "./dist/CounterFresh.app",
      "windows": "./dist/CounterFresh",
      "linux": "./dist/counter-fresh"
    }
  }
}

Controls Controls & Interactions

Action Control / Interaction Description
Add Participant Click + at the bottom Adds a new participant card
Change Score Click + / Increases or decreases score
Edit Name Click name input Edits participant name
Delete Participant Hold the Delete button (850ms) Fills progress bar and shakes before confirming deletion
Cancel Deletion Release before 850ms Drains progress back to 0
Close Desktop App Click top-right ✕ or press Esc Closes the window / exits the application

About

Animated Counter Desktop Application - with Fresh and Deno Desktop

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Contributors

Languages