npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

css-counter-value

v2.0.1

Published

Convert CSS counter() values to integers without JavaScript

Readme

Jane Ori - PropJockey.io

css-counter-value from PropJockey

Convert CSS counter() values to integers without JavaScript.

The limiting feature support required to use this utility is timeline-scope.

css-counter-value Features:

  1. Converting any counter() value into an integer for use in calc().
  2. Passing integer value of a counter UP to parents (Giving parents their child count as a --var).
  3. The integer for a counter value can be set from any counter() contextually and be scoped all the way up on :root so the whole site's CSS can mathematically respond to any counter anywhere.
  4. Counter values 0 to 120 supported.
  5. Since counters can count globally unlike :nth-* selectors, elements can now mathematically respond to their GLOBAL index (<= 120).
  6. Style queries then empower decendants of the counter value's scope to respond in any way you can think of, beyond mathematical manipulation.
  7. Need the counter value's scope itself to respond in non-mathematical ways? Run a mathematical comparitor then convert the resulting bit into a Space Toggle using an animation like shown in the demo window here.
  8. No JavaScript involved.

Installation and Setup

$ npm install css-counter-value

Then include /node_modules/css-counter-value/counter-value.css

OR Use your favorite NPM CDN for small projects

From html:

<link rel="stylesheet" type="text/css" href="https://unpkg.com/css-counter-value@2/counter-value.css">

or directly from your CSS:

@import url(https://unpkg.com/css-counter-value@2/counter-value.css);

Usage

Any element on the page can have a maximum of 8 converted counters hosted in its scope, each counter occupying a variable 1 through 8.

var(--counter-value\\1) var(--counter-value\\2) var(--counter-value\\3) var(--counter-value\\4) var(--counter-value\\5) var(--counter-value\\6) var(--counter-value\\7) var(--counter-value\\8)

For example, a td might host its table-row counter (1) and its table-col counter (2).

Additionally, the table might host the value of the counters of the last td, effectively putting two more counters in scope for the rest of the table.

This rapidly enables the table (or grid, lists, etc) to know how many rows and columns it has without writing complex selectors to account for every potential count.

In this scenario, there would still 4 additional counters-values that could be converted in scope of any td on the table.

css-counter-value Example

If you prefer to dive into live code, check out this codepen.

Given this table, or a similarly structured grid, nested lists, or any other DOM tree (noting the inline styles managing the counter states):

<table>
  <thead>...</thead>
  <tbody style="counter-reset: table-row 0;">
    <tr style="counter-increment: table-row 1; counter-reset: table-col 0;">
      <td style="counter-increment: table-col 1;">
        Who am I, really?
      </td>
      <td style="counter-increment: table-col 1;">
        Who am I, really?
      </td>
    </tr>
    <tr style="counter-increment: table-row 1; counter-reset: table-col 0;">
      <td style="counter-increment: table-col 1;">
        Who am I, really?
      </td>
      <td style="counter-increment: table-col 1;">
        Who am I, really?
      </td>
    </tr>
  </tbody>
</table>

To convert a counter into an integer, add a directive as a descendant of the elment being counted and css-counter-value's built in CSS interpreter will assign the output counter-value\\* vars as directed.

By default, without a matching host in a directive's context, the directive will assign the converted counter-value to var 1 (of 8) on its immediate parent, making the parent element an implicit host for the variable its setting:

<table>
  <thead>...</thead>
  <tbody style="counter-reset: table-row 0;">
    <tr style="counter-increment: table-row 1; counter-reset: table-col 0;">
      <td style="counter-increment: table-col 1;">
        Who am I, really?
      </td>
      <td style="counter-increment: table-col 1;">
        <output data-counter-value style="--counter: table-col;"></output>
        Know Thyself...?
      </td>
    </tr>
    <tr style="counter-increment: table-row 1; counter-reset: table-col 0;">
      <td style="counter-increment: table-col 1;">
        Who am I, really?
      </td>
      <td style="counter-increment: table-col 1;">
        <output data-counter-value style="--counter: table-col;"></output>
        Know Thyself...?
      </td>
    </tr>
  </tbody>
</table>

The two td elements with the data-counter-value directive inside of them now hold a CSS var var(--counter-value\\1) with an integer value of 2.

We set the --counter variable to [the name of the counter] we wish to convert using the style attribute inline, but this can be done from your CSS if you prefer.

To add the row number, we must choose a variable slot not already in scope (other than 1 in this case) and add another directive to the td's. To do this, assign the desired number 1 to 8 to the data-counter-value attribute and set --counter to the table-row counter like so:

  <td style="counter-increment: table-col 1;">
    <output data-counter-value style="--counter: table-col;"></output>
    <output data-counter-value="2" style="--counter: table-row;"></output>
    Know Thyself.
  </td>

The two td elements now implicity host a second CSS var var(--counter-value\\2) with an integer value of 1 or 2 depending on which row is looking.

data-counter-value="7" would mean "set var(--counter-value\\7)'s value to [whatever counter name's value is specified]"

If we want our table to know how many rows and cols it contains, we add a explicit host directive attribute values to the table and, in the final td, two directives that set those values based on the counter() states in their td element's scope:

<table data-counter-value="host:3 host:4">
  ...
  <tbody style="counter-reset: table-row 0;">
    ...
    <tr style="counter-increment: table-row 1; counter-reset: table-col 0;">
      ..
      <td style="counter-increment: table-col 1;">
        <output data-counter-value style="--counter: table-col;"></output>
        <output data-counter-value="2" style="--counter: table-row;"></output>
        <output data-counter-value="3" style="--counter: table-col;"></output>
        <output data-counter-value="4" style="--counter: table-row;"></output>
        Know Thyself.
      </td>

Assuming this is the last td in the table, the table and every element inside of it will have access to var(--counter-value\\3) (which contains and integer count of the total number of columns in the last row) and var(--counter-value\\4) (which contains the integer total count of rows in the table).

Any of the 8 counter-value variables can be hosted on any ancestor, even :root, and be set from any counter context within the host element.

[!NOTE] Even though the table-row counter itself is stored on the tbody, the whole tabel has access to its integer counter value now.

Every td can use both of these total count vars without adding the additional directives because the --counter-value\\3 and --counter-value\\4 vars are inherited from table.

[!CAUTION]
Any hosting element (matching data-counter-value*="host:") must not host the same variable as an ancestor. That is, for any given element's scope, it must not have more than one ancestor hosting the same counter-value variable 1 to 8.

[!CAUTION] Within any given host element's context (descendants), there must not be more than one directive trying to set the same counter-value variable.

[!TIP] If your counters for a specific counter-value variable 1 to 8 are all 0 values, the most likely scenario is a violation of either of the previous two rules.

Any number of sibling elements can host all 8 counter-value variables for their own contexts as long as they don't have ancestors or descendants also trying to host one of the 8 variables.

For example, these three scenarios all break for the same reason:

<div data-counter-value="host:1 host:2 host:3">
  <output data-counter-value="2" style="--counter: global-div-counter;"></output>
  1
  <div>
    <output data-counter-value style="--counter: global-div-counter;"></output>
    <!-- note this ^ is setting var 1, not implicitly on the parent, but explicitly to the ancestor hosting var 1 since it exists -->
    2
  </div>
  <div>
    <output data-counter-value style="--counter: global-div-counter;"></output>
    <!-- note this ^ is setting var 1 to the same host, which is a conflict -->
    3
  </div>
</div>

and, very similar:

<div data-counter-value="host:1 host:2 host:3">
  <output data-counter-value style="--counter: global-div-counter;"></output>
  <!-- note this ^ is setting var 1 to its parent because the parent explicitly hosts var 1 -->
  1
  <div>
    <output data-counter-value style="--counter: global-div-counter;"></output>
    <!-- note this ^ is setting var 1 to the ancestor hosting var 1 since one exists -->
    2
  </div>
</div>

and, the tricky one:

<div data-counter-value="host:2 host:3">
  <output data-counter-value style="--counter: global-div-counter;"></output>
  <!-- note this ^ is setting var 1 to its parent implicitly -->
  1
  <div>
    <output data-counter-value style="--counter: global-div-counter;"></output>
    <!-- note this ^ is going to try setting var1 implicitly to its parent but will fail because an ancestor is implicity already hosting it -->
    2
  </div>
</div>

The following example is completely acceptable - there's a wide range of wild, exciting behavior still possible. We can have all sorts of fun within the established boundaries of this relationship, if you want 😉

<html data-counter-value="host:8">
  ...
  <body style="counter-reset: global-div-counter: 0;">
    <style>div { counter-increment: global-div-counter 1; }</style>

    <div data-counter-value="host:3">
      <output data-counter-value="2" style="--counter: global-div-counter;"></output>
      <!-- var 2, implicitly on parent -->
      1
      <div>
        <output data-counter-value style="--counter: global-div-counter;"></output>
        <!-- var 1, implicitly on parent -->
        2
      </div>
      <div>
        <output data-counter-value style="--counter: global-div-counter;"></output>
        <!-- var 1, implicitly on parent -->
        <output data-counter-value="3" style="--counter: global-div-counter;"></output>
        <!-- var 3, explicitly on an ancestor -->
        3
      </div>
    </div>

    <div data-counter-value="host:3">
      <output data-counter-value="2" style="--counter: global-div-counter;"></output>
      <!-- var 2, implicitly on parent -->
      4
      <div>
        <output data-counter-value style="--counter: global-div-counter;"></output>
        <!-- var 1, implicitly on parent -->
        5
      </div>
      <div>
        <output data-counter-value style="--counter: global-div-counter;"></output>
        <!-- var 1, implicitly on parent -->
        <output data-counter-value="3" style="--counter: global-div-counter;"></output>
        <!-- var 3, explicitly on an ancestor -->
        6
      </div>
    </div>

    <div data-counter-value="host:3">
      <output data-counter-value="2" style="--counter: global-div-counter;"></output>
      <!-- var 2, implicitly on parent -->
      7
      <div>
        <output data-counter-value style="--counter: global-div-counter;"></output>
        <!-- var 1, implicitly on parent -->
        8
      </div>
      <div>
        <output data-counter-value style="--counter: global-div-counter;"></output>
        <!-- var 1, implicitly on parent -->
        9
      </div>
      <div>
        <output data-counter-value style="--counter: global-div-counter;"></output>
        <!-- var 1, implicitly on parent -->
        10
      </div>
      <div>
        <output data-counter-value style="--counter: global-div-counter;"></output>
        <!-- var 1, implicitly on parent -->
        <output data-counter-value="3" style="--counter: global-div-counter;"></output>
        <!-- var 3, explicitly on an ancestor -->
        <output data-counter-value="8" style="--counter: global-div-counter;"></output>
        <!-- var 8, explicitly on :root, giving the whole document access to a variable var(--counter-value\\8) === 11 -->
        11
      </div>
    </div>

Oh, and if you're setting multiple counter-value variables with the same counter source, you can just use one directive instead.

  <div>
    <output data-counter-value style="--counter: global-div-counter;"></output>
    <!-- var 1, implicitly on parent -->
    <output data-counter-value="3" style="--counter: global-div-counter;"></output>
    <!-- var 3, explicitly on an ancestor -->
    <output data-counter-value="8" style="--counter: global-div-counter;"></output>
    <!-- var 8, explicitly on :root, giving the whole document access to a variable var(--counter-value\\8) === 11 -->
    11
  </div>

becomes

  <div>
    <output data-counter-value="1 3 8" style="--counter: global-div-counter;"></output>
    <!-- var 1, implicitly on parent -->
    <!-- var 3, explicitly on an ancestor -->
    <!-- var 8, explicitly on :root, giving the whole document access to a variable var(--counter-value\\8) === 11 -->
    11
  </div>

Have fun!

Open Contact 👽

Please do reach out if you need help with any of this, have feature requests, want to share what you've created, or wish to learn more.

| PropJockey.io | CodePen | DEV Blog | GitHub | Mastodon | | --- | --- | --- | --- | --- | | PropJockey.io | CodePen | DEV Blog | GitHub | Mastodon |

🦋@JaneOri.PropJockey.io

𝕏@Jane0ri