Open-source project
pqina/flip avatar
pqina/flip

Flip is a browser flip clock that hides its own visuals from screen readers

⏳ The online version of the classic flip clock

1,024 stars94 forksJavaScriptMIT

At a glance

What is it?
Flip is a JavaScript flip counter and countdown plugin built on PQINA's Tick engine, usable from a plain script tag, a CDN, npm or a framework, with an accessibility pattern that marks the animated face as aria-hidden and supplies an aria-label instead. It fits a marketing page that wants a clock, and it does not fit an application that needs a real time source rather than a tick.
Who is it for?
Flip fits a developer who wants a mechanical flip animation on a landing page, a countdown or a progress display, and it does not fit a project that needs the counter to be the source of truth for anything. Before using it, decide how the value is driven, because the library animates a number rather than reading a clock, and note that the accessibility pattern in the README hides the flip view from screen readers and requires you to set an aria-label yourself.
Can I use it commercially?
Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
Is it still maintained?
Yes. The repository last received commits 102 days ago.
What is it written in?
Mainly JavaScript, according to GitHub's language statistics.

Answers come from the project's GitHub data, last synced on October 23, 2026, and from our analysis. They are not legal advice.

Editorial analysis

The counter is driven by a value you set, not by the time

Flip is described as an advanced and beautiful flip counter plugin, easy to use and highly flexible, where you can set up a custom counter on your website in minutes. The stated uses are displaying visitor counts, counting down to a special date, or celebrating progress.

The word counter is the important one. Flip renders and animates a number that your code sets, and the demo code in the README shows the pattern. The setup function receives a tick object, and an interval callback increments the value and then updates the accessible label:

js
Tick.helper.interval(function() {

    tick.value++;

    // Set `aria-label` attribute which screenreaders will read instead of HTML content
    tick.root.setAttribute('aria-label', tick.value);

}, 1000);

So the animation is a rendering of a value, and the source of that value is your code. A countdown is a value that decreases over time, and a visitor count is a value that increases. Neither is read from a system clock by the library itself.

That design is what makes the plugin flexible, and it is also the constraint to understand before adopting it. If you need a clock that is always right, including after a tab has been in the background, you are writing that logic yourself, and a counter that drifts is a visible bug on a countdown to a deadline.

Four ways to install it, and the example directory is the fastest start

The setup section assumes the minified CSS and JS files and a folder named flip that you upload yourself. The stylesheet goes in the head of the page:

html
<link href="/flip/flip.min.css" rel="stylesheet">

And the script goes just before the closing body tag:

html
<script src="/flip/flip.min.js"></script>

The paths have to match where you put the files, and after that you can copy the presets from the demo site onto your own page.

The same two files are available from a CDN such as unpkg, with https://unpkg.com/@pqina/flip/dist/flip.min.css and https://unpkg.com/@pqina/flip/dist/flip.min.js, which removes the upload step entirely.

For a project with a build step, the package installs from npm:

code
npm i @pqina/flip --save

and the module is imported as:

js
import Tick from '@pqina/flip';

console.log(Tick);
// logs {supported: true, options: {…}, helper: {…}, data: {…}, DOM: {…}, …}

The default export is the Tick engine, and the logged object shows the shape of the public API with supported, options, helper, data and DOM members.

The quick start route is the example directory. The README says to copy the example directory and use it as a starting point, since it automatically initializes Flip counters on the website and can be used with the presets on the demo site. There are also Codesandbox projects for React and Vue, and a Stackblitz project for the module usage, which are the fastest way to see the plugin in a framework context.

The accessibility pattern hides the animation and exposes a label

The accessibility section is the most technically interesting part of the README, because it deals with a problem the animation creates rather than a feature it adds.

For better compatibility with screenreaders, the flip view has to be hidden using aria-hidden, which prevents the contents from being read out loud, and an aria-label attribute is then used to provide a formatted label instead.

The markup shows the shape:

html
<div class="tick"
     data-value="1234"
     data-did-init="setupFlip">
  
    <!-- Hide visual content from screenreaders with `aria-hidden` -->
    <div data-repeat="true" aria-hidden="true">
        <span data-view="flip"></span>
    </div>
  
</div>

The reasoning is sound. A flip clock renders each digit as a stack of animated half-cards, so a screen reader encountering the raw DOM would read a long sequence of digit fragments rather than the number. Hiding that and exposing a single label is the right call.

The burden falls on the integrator, though. Nothing in the library sets the aria-label for you, and the README's example does it inside the interval callback, which means the label is only correct if you update it on every value change. If you drive the value from somewhere other than a tick, you have to remember to set the label yourself, and a stale label is worse than no label.

The data attributes are the other half of the integration. data-value sets the initial number, data-did-init names the function to call when the element is initialised, data-repeat marks the repeating element and data-view selects the view, in this case flip. The plugin is configured through markup rather than through a constructor, which is what lets a plain script tag work with no JavaScript setup at all.

The engine underneath is Tick, and Flip is the presentation layer

The README points to a companion project and that is the architecture. Flip is built on the Tick engine, and there is a separate link to learn more about Tick and use the Tick API on the product website.

The repository layout confirms it. Alongside src/, dist/, example/ and the usual metadata, there is a tick/ directory, and the package.json files array publishes both dist and tick. The published module is the Tick engine, and the flip view is one of the views it can render.

The separation is a sensible design for a plugin library, and it has a consequence for anyone evaluating the dependency. The thing you install and import is Tick, and Flip is a view within it. That means the public API surface you interact with is Tick's, including Tick.helper.interval, which the accessibility example uses, and tick.root and tick.value. A developer who wants to understand what is available needs to read the Tick documentation, not just the Flip README.

It also means the plugin generalises. Tick is described as the engine that powers Flip, and the repo includes a tick/ directory that ships with the package, so the engine is available to anyone who wants to build a different view or use the engine's data model directly.

The build tooling is worth a mention for anyone maintaining a fork. It is a Gulp pipeline with gulpfile.babel.js, Babel 6, Rollup with rollup-plugin-babel, browser-sync for development, and a set of gulp plugins for Sass, PostCSS with autoprefixer, cssnano, uglify, markdown, nunjucks and zipping. The dist/ directory is committed, so the minified files the script tag references are in the repository.

Where Flip is the wrong tool

The first wrong-tool case is a clock that must be accurate. Because the library renders a value you set, a clock that shows the wrong time is a bug in your code, not in the library. Background tabs throttle timers, and a countdown that relies on decrementing a counter once a second drifts, and it drifts in exactly the way that matters, since the deadline you are counting towards is the one thing that does not move.

The second is an application that treats the counter as data. The counter here is a display. It renders whatever you put in it, and the README's own use cases are visitor counts, countdowns to a date and progress celebrations. If you need the number to be readable by assistive technology as the only source of truth, or to be scraped, or to drive other behaviour, you are already maintaining the value somewhere else and the flip animation is decoration on top.

The third is the accessibility work you inherit. The library does not set an aria-label, so the integration example in the README is a template you have to follow correctly, and a plugin that hides its own content from screen readers is only as accessible as the label you supply. A team without a plan for maintaining that label is better off with plain text.

The fourth is the build toolchain, if you plan to fork. Babel 6, Gulp 3 and browser-sync are the current generation for a project whose last push was on 2026-07-13. The package itself is MIT licensed and works fine as a dependency, since the compiled dist/ files are what you consume, but maintaining a fork means maintaining that pipeline.

Editorial conclusion

Flip fits a developer who wants a mechanical flip animation on a landing page, a countdown or a progress display, and it does not fit a project that needs the counter to be the source of truth for anything. Before using it, decide how the value is driven, because the library animates a number rather than reading a clock, and note that the accessibility pattern in the README hides the flip view from screen readers and requires you to set an aria-label yourself.

Frequently asked questions

How do I install and set up the Flip counter?

Copy flip.min.css into your page head and flip.min.js just before the closing body tag, or load both from unpkg, or install from npm with npm i @pqina/flip --save and import Tick from '@pqina/flip'. The example directory in the repository is the fastest starting point, and there are Codesandbox projects for React and Vue.

Does Flip work with screen readers?

The flip view should be hidden with aria-hidden so its contents are not read out loud, and an aria-label attribute supplied instead to give a formatted label. The README notes this is needed for better compatibility with screenreaders, and the example sets the label from the tick value inside the interval callback.

What engine does the Flip counter use?

Flip is built on PQINA's Tick engine, and the default export of the @pqina/flip package is Tick. The tick/ directory ships in the published files alongside dist, and the API surface you interact with, including Tick.helper.interval, tick.value and tick.root, belongs to that engine.

Does Flip read the system clock?

No. The counter renders a value that your code sets. The README's example increments tick.value in an interval callback and then updates the aria-label, so a countdown or a clock is driven by the integrating code rather than by the library.

Official sources

  1. Issues
  2. License: MIT
  3. pqina/flip on GitHub
  4. Project website
  5. README
Add this badge to your README

If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.

Add this badge to your README

markdown
[![Hysen Labs](https://hysenlabs.com/badge/pqina-flip.svg)](https://hysenlabs.com/projects/pqina-flip)

Was this useful?