Anki Progression [Support Thread]

Progression bar
Support thread for the Anki Progression [Lτ] add-on, which introduces a versatile and customizable progress bar to Anki. Key distinctions from other progress bar add-ons:

Stats:

  • Allows distinguishing between daily reviews rated Again, Hard, Good, and Easy
  • Allows differentiation between New, Learning, and Review cards, as well as further distinction between Learning and Relearning states
  • Accurately calculates New cards count for all decks factoring in daily limits of each subdeck in the tree
  • Can separately display (re)learning cards that are presently due and those that are on cooldown for the current learning step and will become available later in the day
  • Estimates future intraday reviews for new, learning and relearning cards in accordance with learning and relearning steps set in deck settings

Customization:

  • All categories described above can be freely disabled, grouped, merged, and reordered for a complete control over the level-of-detail of the displayed progress (instructions)
  • The whole setup is controlled directly with CSS, making every display property (including overall size, position, colors, animations, etc.) readily adjustable. In particular, it allows breaking the progress bar into sections, wrapping the sections across several lines (see sample presets for different styles)
  • Different appearances can be set up for different Anki windows (for instance, the progress bar can be displayed in a minimal style during the review, while providing a detailed breakdown of complete stats in the main deck screen)
  • The bar is embedded directly into the webview, allowing card templates to tailor the progress visualization, incorporating it seamlessly into their design (example)

What the add-on doesn’t do (so far):

  • Count manually rescheduled cards and cards in filtered decks
  • Take into account non-monotonic learning steps
  • Future steps calculations are based on estimates and can slightly deviate from the actual counts, especially when sibling burying is enabled in the settings. All estimates are convergent, however, and do not display any residual reviews when none left
  • The learn-ahead timer is not taken into account when differentiating between due and on-cooldown cards

Progress bar presets

1. Minimal

The most basic setup displaying the total reviews done in the current day vs total reviews due at the moment

Minimal progress bar

Preset CSS
.prog-segment {
    background: var(--canvas-elevated);
}
  
.prog-section.done {
    background: var(--button-primary-bg);
}
  
.prog-segment.cooldown,
.prog-segment.future {
    display: none;
}

1A. Minimal (strict)

Does not count Again (on all cards) and Hard (on (re)learned cards) towards the daily progress. Additionally displays cards which currently are in a (re)learning phase, but which are not yet due, in the to-do section:

Strict minimal progress bar

Preset CSS
.prog-segment {
    background: var(--canvas-elevated);
}

.prog-segment.done {
    background: red;
}

.prog-segment.again,
.prog-segment.learning.hard {
    display: none;
}

.prog-segment.todo {
    opacity: 0.6;
}

.prog-segment.cooldown {
    background: var(--border-subtle);
}

.prog-segment.future {
    display: none;
}

2. Ratings

Splits the completed portion of the progress bar into sections for each of the Again, Hard, Good, and Easy card ratings. The colors correspond to the Anki’s Stats screen. Takes the number of all future steps for currently (re)learned cards into account for the to-do section.

Ratings progress bar

Preset CSS
#lt-progress-bar {
    top: .25rem;
}

.prog-segment {
    height: 1rem;
}

.prog-segment.done {
    background: var(--rate-color);
}

.prog-segment.todo {
    background: var(--canvas-elevated);
}

.prog-segment.cooldown,
.prog-segment.future {
    background: var(--canvas);
}

.prog-segment.again {
    order: -4;
}
.prog-segment.hard {
    order: -3;
}
.prog-segment.good {
    order: -2;
}
.prog-segment.easy {
    order: -1;
}

2A. Ratings (AnkiDroid)

Similar setup with the color scheme borrowed from AnkiDroid rating buttons:

AnkiDroid ratings progress bar

Preset CSS
#lt-progress-bar {
    top: .25rem;
    box-shadow: 0px 2px 1px -1px rgba(20,20,20,.12),0px 1px 1px 0px rgba(20,20,20,.06),0px 1px 3px 0px rgba(20,20,20,.04);
}

.prog-segment {
    height: 1rem;
}

.prog-segment.done {
    background: var(--rate-color);
}

.prog-segment.todo {
    background: var(--canvas-elevated);
}

.prog-segment.cooldown {
    background: var(--canvas);
}

.prog-segment.future {
    display: none;
}

.prog-segment.again {
    --rate-color: color-mix(in lab, var(--ad-again) 80%, var(--ad-again-text));
    order: -4;
}
.prog-segment.hard {
    --rate-color: color-mix(in lab, var(--ad-hard) 80%, var(--ad-hard-text));
    order: -3;
}
.prog-segment.good {
    --rate-color: color-mix(in lab, var(--ad-good) 80%, var(--ad-good-text));
    order: -2;
}
.prog-segment.easy {
    --rate-color: color-mix(in lab, var(--ad-easy) 80%, var(--ad-easy-text));
    order: -1;
}

3. States

Splits both the completed (vivid) and to-do (faint) sections of the progress bar into segments for each of the three primary New, Learn, Due card categories. The colors correspond to Anki’s review counters:

States progress bar

Preset CSS
#lt-progress-bar {
    top: 4px;
}

.prog-segment.prog-segment {
    order: 0;
    height: 7px;
}

.prog-segment.done {
    background: var(--state-color);
}

.prog-segment.todo {
    background: color-mix(in lab, var(--state-color) 30%, var(--canvas-elevated));
}

.prog-segment.cooldown,
.prog-segment.future {
    display: none;
}

.prog-segment.learn {
    --state-color: var(--prog-relearn);
}

.prog-segment.new.done {
    order: -2;
}
.prog-segment.learn.done {
    order: -1;
}

.prog-segment.todo {
    order: 1;
}
.prog-segment.review.todo {
    order: 2;
}

3A. States (grouped)

Introduces additional category and respecitve supplementary color (gold) to display newly learned cards separately from relearned ones (which keep the Anki default red color). The ordering correlates with Anki’s inner card “type” logic. Additionally shows future-due segments and future (re)learning steps (with increasingly fainter colors):

Grouped states progress bar

Preset CSS
.prog-segment.prog-segment {
    order: 0;
    height: 7px;
}

.prog-segment.done {
    background: var(--state-color);
}

.prog-segment.todo {
    background: color-mix(in lab, var(--state-color) 30%, var(--canvas-elevated));
}

.prog-segment.cooldown {
    opacity: 0.4;
} 

3.5. Combined (ratings + states)

A setup combining ratings (for the completed section of the bar) with card states (for the to-do section):

Combined ratings-states progress bar

Preset CSS
#lt-progress-bar {
    top: .25rem;
    width: 90vw;
    left: 5vw;
    border-radius: 5px;
    overflow: hidden;
}

.prog-segment.prog-segment {
    order: 0;
    height: 1rem;
}

.prog-segment.done {
    background: var(--rate-color);
}

.prog-segment.todo {
    background: color-mix(in lab, var(--state-color) 40%, var(--canvas-elevated));
}

.prog-segment.cooldown,
.prog-segment.future {
    display: none;
}

.prog-segment.again {
    order: -4;
}
.prog-segment.hard {
    order: -3;
}
.prog-segment.good {
    order: -2;
}
.prog-segment.easy {
    order: -1;
}

4. Multi-track states

A setup similar to #3, but with different states laid out as separate rows:

Multi-row states progress bar

Preset CSS
#lt-progress-bar span {
    flex-basis: 101%;
}

.prog-segment.prog-segment {
    order: 0;
}

.prog-segment:not(.empty) {
    min-width: 1px;
}

.prog-segment.done {
    background: var(--state-color);
}

.prog-segment.todo {
    background: color-mix(in lab, var(--state-color) 30%, var(--canvas-elevated));
}

.prog-segment.future,
.prog-segment.cooldown {
    display: none;
}

.prog-segment.learn {
    --state-color: var(--prog-relearn);
}

.prog-segment.learn.done {
    order: 1;
}
.prog-segment.learn.todo {
    order: 2;
}
.prog-segment.review + span {
    order: 3;
}
.prog-segment.review {
    order: 4;
}

#lt-progress-bar:not(:has(.prog-segment.new:not(.empty))) .prog-segment.new,
#lt-progress-bar:not(:has(.prog-segment.learn:not(.empty):not(.cooldown):not(.future))) .prog-segment.learn,
#lt-progress-bar:not(:has(.prog-segment.review:not(.empty))) .prog-segment.review {
    height: 0;
}

.prog-segment.animated {
    transition-property: flex-grow, height;
}

4A. Multi-track states (+learning)

Multi-track version of #3A, adds optional gaps between the progress segments:

Multi-row states progress bar with a separate learning section

Preset CSS
#lt-progress-bar span {
    flex-basis: 101%;
}

.prog-segment.prog-segment {
    order: 0;
}

.prog-segment.done {
    background: var(--state-color);
}

.prog-segment.todo {
    background: color-mix(in lab, var(--state-color) 30%, var(--canvas-elevated));
}

.prog-segment.cooldown {
    opacity: 0.4;
}

.prog-segment.new:not(.empty):is(.prog-segment.new:not(.empty) ~ .prog-segment),
.prog-segment.learn:not(.re):not(.empty):is(.prog-segment.learn:not(.re):not(.empty) ~ .prog-segment),
.prog-segment.review:not(.empty):is(.prog-segment.review:not(.empty) ~ .prog-segment),
.prog-segment.re.learn:not(.empty):is(.prog-segment.re.learn:not(.empty) ~ .prog-segment) {
    margin-left: 1px;
}

.prog-segment.new:has(.prog-segment.learn:not(.re):not(.empty)),
.prog-segment.learn:not(.re):has(.prog-segment.review:not(.empty)),
.prog-segment.review:has(.prog-segment.re.learn:not(.empty)) {
    margin-bottom: 1px;
}

#lt-progress-bar:not(:has(.prog-segment.new:not(.empty))) .prog-segment.new,
#lt-progress-bar:not(:has(.prog-segment.learn:not(.re):not(.empty))) .prog-segment.learn:not(.re),
#lt-progress-bar:not(:has(.prog-segment.review:not(.empty))) .prog-segment.review,
#lt-progress-bar:not(:has(.prog-segment.re.learn:not(.empty))) .prog-segment.re.learn {
    height: 0;
}

.prog-segment.animated {
    transition-property: flex-grow, height;
}

4B. Two-track states

Groups new cards with newly learned (both correspond to the “Learn” state in Anki’s “Card Info” logic), as well as review cards with relearned:

Two-row states progress bar

Preset CSS
#lt-progress-bar:has(:is(.new, .learn:not(.re)):not(.empty)):has(:is(.review,.re.learn):not(.empty)) {
    gap: 1px 0px;
}

#lt-progress-bar span:is(.new + span) {
    flex-basis: 101%;
}

.prog-segment.prog-segment {
    order: 0;
    height: 8px;
}

.prog-segment.done {
    background: var(--state-color);
}

.prog-segment.todo {
    background: color-mix(in lab, var(--state-color) 30%, var(--canvas-elevated));
}

.prog-segment.new.done {
    order: -4;
}
.prog-segment.learn.done {
    order: -3;
}
.prog-segment.new.todo {
    order: -2;
}
.prog-segment.learn.todo {
    order: -1;
}

.prog-segment.review.done {
    order: 1;
}
.prog-segment.re.done {
    order: 2;
}
.prog-segment.review.todo {
    order: 3;
}
.prog-segment.re.todo {
    order: 4;
}

.prog-segment.cooldown {
    opacity: 0.4;
}

.prog-segment.future {
    display: none;
}

#lt-progress-bar:not(:has(.prog-segment:is(.new, .learn:not(.re)):not(.empty))) .prog-segment:is(.new, .learn:not(.re)),
#lt-progress-bar:not(:has(.prog-segment:is(.review,.re.learn):not(.empty))) .prog-segment:is(.review,.re.learn) {
    height: 0;
}

5. Comprehensive

Provides the most detailed progress overview, displaying all combinations of card states, ratings, and readiness as individual segments:

Comprehensive progress bar

Preset CSS
#lt-progress-bar {
    top: .25rem;
    width: 90vw;
    left: 5vw;
    border-radius: 5px;
    overflow: hidden;
}

#lt-progress-bar span {
    flex-basis: 101%;
}

.prog-segment.prog-segment {
    order: 0;
}

.prog-segment:not(.empty) {
    min-width: 1px;
}

.prog-segment.done {
    background: color-mix(in lab, var(--rate-color) 80%, var(--state-color));
}

.prog-segment.todo {
    background: linear-gradient(to right, color-mix(in lab, var(--state-color) 60%, var(--canvas-elevated)), var(--state-color));
}

.prog-segment.cooldown {
    opacity: 0.6;
}

#lt-progress-bar:not(:has(.prog-segment.new:not(.empty))) .prog-segment.new {
    height: 0;
}
#lt-progress-bar:not(:has(.prog-segment.learn:not(.re):not(.empty))) .prog-segment.learn:not(.re) {
    height: 0;
}
#lt-progress-bar:not(:has(.prog-segment.review:not(.empty))) .prog-segment.review {
    height: 0;
}
#lt-progress-bar:not(:has(.prog-segment.re.learn:not(.empty))) .prog-segment.re.learn {
    height: 0;
}

.prog-segment.animated {
    transition-property: flex-grow, height;
}

5A. Comprehensive (smooth)

Alters the previous setup smoothing the transitions between individual segments and sacrificing some technical precision of the graph for a bit of aesthetic quality:

Smooth comprehensive progress bar

Preset CSS
#lt-progress-bar span {
    flex-basis: 101%;
}

.prog-segment.prog-segment {
    order: 0;
}

.prog-segment:not(.empty) {
    min-width: 1px;
}

.prog-segment.future,
.prog-segment.cooldown {
    opacity: 1;
}

.prog-segment.again {
    background: linear-gradient(to right, var(--stat-again), var(--stat-hard));
}
.prog-segment.hard {
    background: linear-gradient(to right, var(--stat-hard), var(--stat-good));
}
.prog-segment.good {
    background: linear-gradient(to right, var(--stat-good), var(--stat-easy));
}
.prog-segment.easy {
    background: linear-gradient(to right, var(--stat-easy), var(--prog-done));
}

.prog-segment.todo {
    background: linear-gradient(to right, var(--state-color), color-mix(in lab, var(--state-color) 60%, transparent));
}
.prog-segment.cooldown {
    background: linear-gradient(to right, color-mix(in lab, var(--state-color) 60%, transparent), color-mix(in lab, var(--state-color) 40%, transparent));
}
.prog-segment.future {
    background: linear-gradient(to right, color-mix(in lab, var(--state-color) 40%, transparent), color-mix(in lab, var(--state-color) 10%, transparent));
}

#lt-progress-bar:not(:has(.prog-segment.new:not(.empty))) .prog-segment.new {
    height: 0;
}
#lt-progress-bar:not(:has(.prog-segment.learn:not(.re):not(.empty))) .prog-segment.learn:not(.re) {
    height: 0;
}
#lt-progress-bar:not(:has(.prog-segment.review:not(.empty))) .prog-segment.review {
    height: 0;
}
#lt-progress-bar:not(:has(.prog-segment.re.learn:not(.empty))) .prog-segment.re.learn {
    height: 0;
}

.prog-segment.animated {
    transition-property: flex-grow, height;
}

How to set up

  1. Install the add-on (ToolsAdd-onsGet Add-ons... → paste “174803219” (the add-on id) → Ok → restart Anki)
  2. Open the add-on folder (ToolsAdd-ons → select “Anki Progression” → View Files)
  3. Open the “user_files” folder
  4. Edit the “common_styles.css” file in any text editor and copy-paste the desired preset CSS inside
  5. (optional) adjust the colors or any other parameters in the text
  6. Save and refresh Anki window (full restart is not required; if refreshing didn’t work, simply repeating it a few times should suffice for the changes to take effect)

Any of the “home_styles.css”, “deck_styles.css”, or “reviewer_styles.css” can be modified instead of the “common_styles.css” to selectively change the appearance of the progress bar in the respective Anki windows. Alternatively, the presets can be pasted into the styling tab of a card template to only apply the chosen setup when any of the template cards are being reviewed.

General customization instructions


Classes

Progress bar segments are marked by .done and .todo classes for the completed reviews and the sections counting the cards that are yet to be studied respectively. Completed segments are further marked by .again, .hard, .good, and .easy to distinguish between the different ratings assigned during the review.
Similarly .cooldown marks .todo segments counting reviews that will become available later in the day (cards which have already been reviewed and now are waiting for their next (re)learning step) and the .future class marks segments counting all future intraday (re)learning steps specified in deck settings that will come after the step a card is currently on. .todo without the two additional classes marks cards that are ready to be studied right now.
The states of done and todo cards counted by each segment are indicated by the .new, .learn, and .review classes. To further distinguish between learned and relearned cards, the later ones have additional .re class.

Full HTML source code
  <div id="lt-progress-bar">
      <div class="prog-segment new done again"></div>
      <div class="prog-segment new done hard"></div>
      <div class="prog-segment new done good"></div>
      <div class="prog-segment new done easy"></div>
      <div class="prog-segment new todo"></div>
      <span></span>
      <div class="prog-segment learn done again"></div>
      <div class="prog-segment learn done hard"></div>
      <div class="prog-segment learn done good"></div>
      <div class="prog-segment learn done easy"></div>
      <div class="prog-segment learn todo"></div>
      <div class="prog-segment learn todo cooldown"></div>
      <div class="prog-segment learn todo future"></div>
      <span></span>
      <div class="prog-segment review done again"></div>
      <div class="prog-segment review done hard"></div>
      <div class="prog-segment review done good"></div>
      <div class="prog-segment review done easy"></div>
      <div class="prog-segment review todo"></div>
      <span></span>
      <div class="prog-segment re learn done again"></div>
      <div class="prog-segment re learn done hard"></div>
      <div class="prog-segment re learn done good"></div>
      <div class="prog-segment re learn done easy"></div>
      <div class="prog-segment re learn todo"></div>
      <div class="prog-segment re learn todo cooldown"></div>
      <div class="prog-segment re learn todo future"></div>
  </div>

Segment colors

Colors are controlled via the background property. To target specific progress segments or groups of segments the classes described above can be appended to the general .prog-segment class. For example, to change the color of all learning reviews completed today, the following declaration should be added to the styles:

.prog-segment.done.learn {
    background: RebeccaPurple;
}

(this will change the color for both learned and relearned cards, regardless of the rating they received during the study)
The colors can be specified using names from this list, or by copy-pasting their HEX code from a color-picker.

Setting the same color to multiple adjacent segments effectively merges them together and makes them functionally indistinguishable from a single segment.

Disabling segments

To remove certain components of the bar, display: none can be applied. For example:

.prog-segment.again {
    display: none;
}

removes reviews graded Again from the daily progress for all card states, while

.prog-segment.re.learn.future {
    display: none;
}

specifically disables display of future relearning steps.

Reordering segments

The progress bar uses flexbox and its associated properties to control the layout. In particular, the order property can be employed to move the segments around. Elements with lesser values are located to the left of the elements with higher values, elements with the same order value are sorted according to their order in the source HTML code (see above). For example, to move new cards to be shown after all the other segments, the following declaration should be added:

.prog-segment.new {
    order: 2;
}

Note that some elements have order: 1 and order: -1 in the default progress bar style. All order values can be reset with the following declaration:

.prog-segment.prog-segment { /* double selector for increased specificity */
    order: 0;

(the reset should be placed before all assignments of the customized order values to avoid resetting them too)

Grouping segments

For breaking the layout into multiple rows, the progress bar includes several <span> elements, which can be activated by adding

#lt-progress-bar span {
    flex-basis: 101%; /* > 100% to ensure pushing of potential zero-width segments to the next line */
}

To target individual breaks, the selector should be changed to #lt-progress-bar span:nth-of-type(1) with the digit indicating the number of the <span> in the HTML code.

The location of the line breaks can be controlled with the same order property described above.

Weights

On top of the ability to turn certain segments off and on completely, the weights of the enabled sections can be changed in a continuous fashion. For example, adding the following declaration will make the reviews rated Again to contribute to the daily progress only half as much as everything else:

.prog-segment.again {
    flex-grow: calc(0.5 * var(--count));
}

Position

Position of the bar can be controlled directly through the constituent inset properties. Declaration for moving the bar to the bottom of the page, for example:

#lt-progress-bar {
    bottom: 0;
}

By default, the progress bar appears in the fixed place. Declaration for embedding it inside the overall layout and making it scrollable together with the whole page:

#lt-progress-bar {
    position: static;
}

Width

By default, the width of the bar is set in viewport units to avoid it being compressed when a vertical scrollbar appears on the page. To change the width of the progress bar while keeping the centering, the following two rules can be added:

#lt-progress-bar {
    width: 80vw;
    left: 10vw; /* = (100 - 80) / 2 */
}

To set the width in the absolute units instead:

#lt-progress-bar {
    width: 420px;
    margin: 0 auto;
    right: 0;
}

(with absolute width the compression by the scrollbar is not an issue, so the centering can be automated)

Height

Because the height of the bar depends on the number of rows in the layout, it is better controlled via heights of individual segments instead of the bar as a whole:

.prog-segment {
    height: 15px;
}

Decorations

Some additional useful styles, such as rounding the borders and dropping a shadow:

#lt-progress-bar {
    border-radius: 4px;
    overflow: hidden;
    
    drop-shadow: 1px 1px 2px #0005;
}

Even more examples of advanced styling are presented below.

Decorated presets

Extra styles for the progress bar with fancier effects and animations

6. Relief

A variation on #2 with a simple frame and a beveled backdrop:

Preset CSS
#lt-progress-bar {
    position: static;
    width: 500px;
    margin: 1rem auto;
    border-radius: 7px;
    overflow: hidden;
    padding: 5px;
    box-shadow: inset 0 1px 2px #0004, 0 1px #fff2;
    background: color-mix(in lab, var(--border-subtle) 50%, var(--canvas-elevated));
}

.prog-segment.prog-segment {
    order: 0;
    height: 20px;
}

.prog-segment.done {
    background: var(--rate-color);
}

.prog-segment.again:not(.empty):not(.prog-segment.again:not(.empty) ~ .prog-segment),
#lt-progress-bar:not(:has(.prog-segment.again:not(.empty))) .prog-segment.hard:not(.empty):not(.prog-segment.hard:not(.empty) ~ .prog-segment),
#lt-progress-bar:not(:has(.prog-segment:is(.again,.hard):not(.empty))) .prog-segment.good:not(.empty):not(.prog-segment.good:not(.empty) ~ .prog-segment),
#lt-progress-bar:not(:has(.prog-segment:is(.again,.hard,.good):not(.empty))) .prog-segment.easy:not(.empty):not(.prog-segment.easy:not(.empty) ~ .prog-segment) {
    border-top-left-radius: 5px;
    border-bottom-left-radius: 5px;
}

.prog-segment.easy:not(.empty):not(:has(~ .prog-segment.easy:not(.empty))),
#lt-progress-bar:not(:has(.prog-segment.easy:not(.empty))) .prog-segment.good:not(.empty):not(:has(~ .prog-segment.good:not(.empty))),
#lt-progress-bar:not(:has(.prog-segment:is(.easy,.good):not(.empty))) .prog-segment.hard:not(.empty):not(:has(~ .prog-segment.hard:not(.empty))),
#lt-progress-bar:not(:has(.prog-segment:is(.easy,.good,.hard):not(.empty))) .prog-segment.again:not(.empty):not(:has(~ .prog-segment.again:not(.empty))) {
    border-top-right-radius: 5px;
    border-bottom-right-radius: 5px;
}

.prog-segment.todo {
    background: var(--border-subtle);
    opacity: 0;
}

.prog-segment.cooldown,
.prog-segment.future {
    display: none;
}

.prog-segment.again {
    order: -4;
}
.prog-segment.hard {
    order: -3;
}
.prog-segment.good {
    order: -2;
}
.prog-segment.easy {
    order: -1;
}

7. Glow

Displays stats in a similar fashion to #3, but with a distinct “Learned” section from #3A and without future reviews coloring:

Preset CSS (Animated)
#lt-progress-bar {
    position: relative;
    width: 500px;
    margin: 1rem auto;
    border-radius: 20px;
    padding: 4px;
    box-shadow: 0 0 20px 4px #0005,
                inset 0 0 10px 2px #0002;
    background: color-mix(in lab, #202020 50%, var(--border-subtle));
}

#lt-progress-bar::before,
#lt-progress-bar::after {
    position: absolute;
    content: '';
}

#lt-progress-bar::before {
    inset: 0;
    background: linear-gradient( to bottom, white, transparent 40%, transparent 60%, white);
    margin: 2px;
    opacity: 0.2;
    border-radius: 20px;
}

#lt-progress-bar::after {
    top: 30%;
    height: 60%;
    background: linear-gradient( to bottom, white, white 10%, transparent 50%);
    left: 2%;
    right: 10%;
    opacity: 0.4;
    border-radius: 10px/20px;
    mask-image: linear-gradient(to right, black 60%, transparent 100%);
    mask-repeat: no-repeat;
    mask-size: 100% 100%;
}

.prog-segment.prog-segment {
    order: 0;
    height: 8px;
    position: relative;
}

.prog-segment.done {
    background: var(--state-color);
    box-shadow: 0 0 7px var(--state-color);
}

@keyframes flicker {
  from {
    box-shadow: 0 0 2px var(--glow-color),
                0 0 8px var(--state-color),
                0 0 18px color-mix(in lab, var(--state-color) 60%, darkblue);
  }
  to {
    box-shadow: 0 0 4px 1px var(--glow-color),
                0 0 12px 2px color-mix(in lab, var(--state-color) 25%, var(--glow-color)),
                0 0 20px 2px color-mix(in lab, var(--state-color) 60%, darkblue);
  }
}

.prog-segment.done:not(.empty)::before {
    content: '';
    position: absolute;
    inset: 0;
    display: block;
    animation: flicker 4s ease-in-out infinite alternate;
    z-index: -1;
}
.prog-segment.learn::before {
    animation-duration: 3.812s;
}
.prog-segment.re.learn::before {
    animation-duration: 3.1415s;
}
.prog-segment.new::before {
    animation-duration: 4.2423s;
}

.prog-segment:not(.empty):not(.prog-segment:not(.empty) ~ .prog-segment)::before,
.prog-segment:not(.empty):not(.prog-segment:not(.empty) ~ .prog-segment) {
    border-radius: 10px 0 0 10px;
}
.prog-segment.done:not(.empty):not(:has(~ .prog-segment.done:not(.empty)))::before,
.prog-segment.done:not(.empty):not(:has(~ .prog-segment.done:not(.empty))) {
    border-radius: 0 10px 10px 0;
}

.prog-segment.todo {
    background: var(--border-subtle);
    opacity: 0;
}

.prog-segment.future {
    display: none;
}

.prog-segment.done {
    order: -2;
}

.prog-segment.new {
    --state-color: color-mix(in lab, var(--prog-new) 50%, #3b82f6);
    --glow-color: var(--prog-new);
}
.prog-segment.learn {
    --glow-color: yellow;
}
.prog-segment.re.learn {
    --state-color: color-mix(in lab, var(--prog-relearn) 50%, #dc2626);
    --glow-color: var(--prog-relearn);
}
.prog-segment.review {
    --state-color: color-mix(in lab, var(--prog-review) 50%, #16a34a);
    --glow-color: var(--prog-review);
}

The smooth animations only supported in the latest Anki (26.05). For earlier versions (or to simply get rid of the redundant motion) a non-animated version is recommended:

Preset CSS
#lt-progress-bar {
    position: relative;
    width: 500px;
    margin: 1rem auto;
    border-radius: 20px;
    padding: 4px;
    box-shadow: 0 0 20px 4px #0005,
                inset 0 0 10px 2px #0002;
    background: color-mix(in lab, #202020 50%, var(--border-subtle));
}

#lt-progress-bar::before,
#lt-progress-bar::after {
    position: absolute;
    content: '';
}

#lt-progress-bar::before {
    inset: 0;
    background: linear-gradient( to bottom, white, transparent 40%, transparent 60%, white);
    margin: 2px;
    opacity: 0.2;
    border-radius: 20px;
}

#lt-progress-bar::after {
    top: 30%;
    height: 60%;
    background: linear-gradient( to bottom, white, white 10%, transparent 50%);
    left: 2%;
    right: 10%;
    opacity: 0.4;
    border-radius: 10px/20px;
    mask-image: linear-gradient(to right, black 60%, transparent 100%);
    mask-repeat: no-repeat;
    mask-size: 100% 100%;
}

.prog-segment.prog-segment {
    order: 0;
    height: 8px;
    position: relative;
}

.prog-segment.done {
    background: var(--state-color);
    box-shadow: 0 0 7px var(--state-color);
}

.prog-segment.done:not(.empty)::before {
    content: '';
    position: absolute;
    inset: 0;
    display: block;
    box-shadow: 0 0 4px var(--glow-color),
                0 0 10px 1px var(--state-color),
                0 0 19px 1px color-mix(in lab, var(--state-color) 60%, darkblue);
    z-index: -1;
}
.prog-segment.learn::before {
    animation-duration: 3.812s;
}
.prog-segment.re.learn::before {
    animation-duration: 3.1415s;
}
.prog-segment.new::before {
    animation-duration: 4.2423s;
}

.prog-segment:not(.empty):not(.prog-segment:not(.empty) ~ .prog-segment)::before,
.prog-segment:not(.empty):not(.prog-segment:not(.empty) ~ .prog-segment) {
    border-radius: 10px 0 0 10px;
}
.prog-segment.done:not(.empty):not(:has(~ .prog-segment.done:not(.empty)))::before,
.prog-segment.done:not(.empty):not(:has(~ .prog-segment.done:not(.empty))) {
    border-radius: 0 10px 10px 0;
}

.prog-segment.todo {
    background: var(--border-subtle);
    opacity: 0;
}

.prog-segment.future {
    display: none;
}

.prog-segment.done {
    order: -2;
}

.prog-segment.new {
    --state-color: color-mix(in lab, var(--prog-new) 50%, #3b82f6);
    --glow-color: var(--prog-new);
}
.prog-segment.learn {
    --glow-color: yellow;
}
.prog-segment.re.learn {
    --state-color: color-mix(in lab, var(--prog-relearn) 50%, #dc2626);
    --glow-color: var(--prog-relearn);
}
.prog-segment.review {
    --state-color: color-mix(in lab, var(--prog-review) 50%, #16a34a);
    --glow-color: var(--prog-review);
}

8. Anki particles

A simple progress bar distinguishing only between completed reviews and due cards (same as #1). Animation has an extra effect on reaching full completion:

Progress animation

Preset CSS (Animated)
#lt-progress-bar {
    position: relative;
    width: 500px;
    margin: 1rem auto;
    border-radius: 20px;

    overflow: hidden;
    box-shadow: 0 0 20px 4px #0005,
                inset 0 0 10px 2px #0002;
    background: color-mix(in lab, #202020 50%, var(--border-subtle));
}

#lt-progress-bar::before,
#lt-progress-bar::after {
    position: absolute;
    content: '';
}

#lt-progress-bar::before {
    inset: 0;
    background: linear-gradient( to bottom, white, transparent 40%, transparent 60%, white);
    margin-bottom: 2px;
    opacity: 0.1;
    border-radius: 20px;
}

#lt-progress-bar::after {
    top: 20%;
    height: 60%;
    background: linear-gradient( to bottom, white, white 10%, transparent 50%);
    left: 2%;
    right: 10%;
    opacity: 0.2;
    border-radius: 10px/20px;
    mask-image: linear-gradient(to right, black 60%, transparent 100%);
    mask-repeat: no-repeat;
    mask-size: 100% 100%;
}


.prog-segment.prog-segment {
    order: 0;
    height: 24px;
}

.prog-segment.done {
    background: linear-gradient(to bottom, var(--button-primary-bg), royalblue 50%, var(--button-primary-disabled));
}

.prog-segment:not(.empty):not(.prog-segment:not(.empty) ~ .prog-segment) {
    border-radius: 10px 0 0 10px;
}

.prog-segment.done:not(.empty):not(:has(~ .prog-segment.done:not(.empty))) {
    border-radius: 0 5px 5px 0;
    position: relative;
}
.prog-segment.done:not(.empty):not(:has(~ .prog-segment.done:not(.empty)))::before,
.prog-segment.done:not(.empty):not(:has(~ .prog-segment.done:not(.empty)))::after {
    border-radius: 0 5px 5px 0;
    content: '';
    display: block;
    height: 100%;
    right: 0;
    position: absolute;
    width: 50px;
}

.prog-segment.done:not(.empty):not(:has(~ .prog-segment.done:not(.empty)))::after {
    background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' viewBox='-100 0 200 100'><style>use{transform-box:fill-box;transform-origin: center;animation: particle var(--dur) infinite linear;animation-delay: calc(-15.6512 * var(--dur))} @keyframes particle {from {opacity: 0; rotate: calc(1*var(--rot));scale: calc(var(--scale,1)*50%25);translate:50%25 var(--y);} 10%25{opacity: 0.65} to {rotate: calc(2*var(--rot));translate: calc(-1 * var(--dist)) var(--y);scale: 0%;opacity: .25}}</style><defs><path id='ankistar' d='M55.077,11.428l6.308,15.857c1.463,4.503,5.66,7.552,10.395,7.552l17.03,1.1c5.171,0,7.321,6.617,3.138,9.657l-13.131,10.899c-3.831,2.783-5.434,7.717-3.97,12.22l4.217,16.536c1.598,4.918-4.031,9.008-8.214,5.968l-14.424-9.12c-3.831-2.783-9.018-2.783-12.849,0l-14.424,9.12c-4.183,3.039-9.812-1.05-8.214-5.968l4.217-16.536c1.463-4.503-.14-9.437-3.97-12.22l-13.131-10.899c-4.183-3.039-2.033-9.657,3.138-9.657l17.03-1.1c4.735,0,8.932-3.049,10.395-7.552l6.308-15.857c1.598-4.918,8.556-4.918,10.154,0Z' stroke='%23aef' fill='none' stroke-width='10' /></defs><use href='%23ankistar' style='--dur: 1s; --dist: 140px; --y: 50px; --rot: 100deg; --scale: 1.5;'></use><use href='%23ankistar' style='--dur: 1.414s; --dist: 210px; --y: -20px; --rot: -150deg'></use><use href='%23ankistar' style='--dur: 1.134s; --dist: 190px; --y: -40px; --rot: -120deg; --scale: 1.25;'></use><use href='%23ankistar' style='--dur: 0.791s; --dist: 170px; --y: 30px; --rot: -90deg'></use><use href='%23ankistar' style='--dur: 1.319s; --dist: 190px; --y: 5px; --rot: 125deg; --scale: 0.95;'></use><use href='%23ankistar' style='--dur: 0.819s; --dist: 160px; --y: -15px; --rot: -222deg'></use><use href='%23ankistar' style='--dur: 0.914s; --dist: 195px; --y: 65px; --rot: 302deg'></use><use href='%23ankistar' style='--dur: 1.314s; --dist: 175px; --y: 35px; --rot: 122deg; --scale: 1.75;'></use></svg>");
    background-repeat: no-repeat;
    background-size: contain;
    background-position: right center;
    opacity: 1;

}
@keyframes outerGlow {
  from {
    box-shadow:  0 0 3px 2px white,
                 0 0 5px 5px cyan,
                 0 0 12px 12px royalblue,
                 0 0 18px 20px darkblue;
  }
  to {
    box-shadow:  0 0 2px 2px #def,
                 0 0 4px 5px cyan,
                 0 0 8px 11px royalblue,
                 0 0 14px 17px darkblue;
  }
}
@keyframes innerGlow {
  from {
    opacity: 1;
  }
  to {
    opacity: .9;
  }
}
.prog-segment.done:not(.empty):not(:has(~ .prog-segment.done:not(.empty)))::before {
    background: linear-gradient(to left, #fffc, #4dfa 12px, transparent 25px);
    filter: blur(1px);
    width: 100vw;
    animation: outerGlow 4s infinite alternate ease-in-out,
               innerGlow 1.15s infinite alternate ease-in-out;
}

#lt-progress-bar:not(:has(.todo:not(.empty))) {
    filter: hue-rotate(-80deg);
    transition: filter .5s ease;
}

.prog-segment.todo {
    background: var(--border-subtle);
    opacity: 0;
}

.prog-segment.cooldown,
.prog-segment.future {
    display: none;
}

.prog-segment.done {
    order: -2;

Setup instructions for all presets