{"_id":"@bskyprism/character-counter","_rev":"3-f67060ea615cefe2a1a8af7c6fd8d562","name":"@bskyprism/character-counter","dist-tags":{"latest":"0.0.2"},"versions":{"0.0.0":{"name":"@bskyprism/character-counter","version":"0.0.0","author":{"url":"https://nichoth.com","name":"nichoth","email":"nichoth@nichoth.com"},"license":"SEE LICENSE IN LICENSE","_id":"@bskyprism/character-counter@0.0.0","maintainers":[{"name":"nichoth","email":"nichoth@gmail.com"}],"dist":{"shasum":"1e89121a5353a06310530358610473b7a7e84cd3","tarball":"https://registry.npmjs.org/@bskyprism/character-counter/-/character-counter-0.0.0.tgz","fileCount":14,"integrity":"sha512-iIxsg4/zdpGsGgN9n/7ApFcGKgEjCwfvTzf/lszvNGxb4n3f1BR9q5OqRDLcvegOPThww3XrpBMQgdsL7sMr4w==","signatures":[{"sig":"MEUCIFh4DeRmIaifiiTc+qxGWzmAsKN2hHmXILe6yqxwCBDBAiEA1jp+Knmy/Xmh4300XwLpeWmYJ0VVgzgMKSi8pWpo2qc=","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":101014},"main":"dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","require":"./dist/index.cjs"},"./*":{"import":"./dist/*.js","require":"./dist/*.cjs"},"./css":"./dist/index.css","./min/*":{"import":"./dist/*.min.js","require":"./dist/*.min.cjs"},"./min/css":"./dist/index.min.css"},"gitHead":"2270a1bbaebc9b0a8cef409ce1c8c778ecd8e981","scripts":{"toc":"markdown-toc --maxdepth 3 -i README.md","lint":"eslint \"./**/*.{ts,js}\"","test":"esbuild --bundle ./test/index.ts | tapout","build":"mkdir -p ./dist && rm -rf ./dist/* && npm run build-cjs && npm run build-esm && npm run build-esm:min && npm run build-css && npm run build-css:min","start":"vite","version":"npm run toc && auto-changelog -p --template keepachangelog --breaking-pattern 'BREAKING CHANGE:' && git add CHANGELOG.md README.md","build-cjs":"esbuild src/*.ts --format=cjs --keep-names --tsconfig=tsconfig.build.json --outdir=./dist --out-extension:.js=.cjs --sourcemap","build-css":"npx lightningcss --nesting src/index.css -o dist/index.css","build-esm":"esbuild src/*.ts --format=esm --metafile=dist/meta.json --keep-names --tsconfig=tsconfig.build.json --outdir=./dist --sourcemap && tsc --emitDeclarationOnly --project tsconfig.build.json --outDir dist","build-docs":"typedoc --tsconfig tsconfig.build.json ./src/index.ts","preversion":"npm run lint && stylelint src/*.css","postversion":"git push --follow-tags && npm publish","build-css:min":"npx lightningcss --nesting --minify src/index.css -o dist/index.min.css","build-esm:min":"esbuild ./src/*.ts --format=esm --keep-names --bundle --tsconfig=tsconfig.build.json --minify --out-extension:.js=.min.js --outdir=./dist --sourcemap","build-example":"mkdir -p ./public && rm -rf ./public/* && NODE_ENV=development vite --mode development --base=\"/character-counter\" build","prepublishOnly":"npm run build"},"_npmUser":{"name":"nichoth","email":"nichoth@gmail.com"},"_npmVersion":"11.6.1","description":"[![tests](https://img.shields.io/github/actions/workflow/status/bskyprism/character-counter/nodejs.yml?style=flat-square)](https://github.com/bskyprism/character-counter/actions/workflows/nodejs.yml) [![types](https://img.shields.io/npm/types/@bskyprism/c","directories":{},"_nodeVersion":"22.18.0","dependencies":{"@substrate-system/web-component":"^0.0.38"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.3.0","eslint":"^8.57.0","esbuild":"^0.27.2","typedoc":"^0.28.1","axe-core":"^4.11.1","tap-spec":"^5.0.0","tape-run":"^11.0.0","stylelint":"^16.19.1","typescript":"^5.9.3","browserslist":"^4.28.1","lightningcss":"^1.30.2","markdown-toc":"^1.2.0","auto-changelog":"^2.4.0","lightningcss-cli":"^1.30.2","@substrate-system/dom":"^0.1.13","eslint-config-standard":"^17.1.0","@substrate-system/debug":"^0.9.32","@substrate-system/tapout":"^0.0.33","@substrate-system/tapzero":"^0.10.16","@typescript-eslint/parser":"^8.7.0","stylelint-config-standard":"^39.0.1","@typescript-eslint/eslint-plugin":"^8.7.0"},"_npmOperationalInternal":{"tmp":"tmp/character-counter_0.0.0_1768531540349_0.6044572506673975","host":"s3://npm-registry-packages-npm-production"}},"0.0.1":{"name":"@bskyprism/character-counter","version":"0.0.1","author":{"url":"https://nichoth.com","name":"nichoth","email":"nichoth@nichoth.com"},"license":"SEE LICENSE IN LICENSE","_id":"@bskyprism/character-counter@0.0.1","maintainers":[{"name":"nichoth","email":"nichoth@gmail.com"}],"dist":{"shasum":"fc4050dc052c66dfe4d19889fbabcb76fa41120c","tarball":"https://registry.npmjs.org/@bskyprism/character-counter/-/character-counter-0.0.1.tgz","fileCount":14,"integrity":"sha512-wN57VHJIwDng5LFp0olit4OjlF+n6KKobUH9ay2Ut4KazXRWV1iJ1rqzyrRWZDoDg9hSv5zmXuXr5vOsVwdYJQ==","signatures":[{"sig":"MEQCIHOIl28UZnqUCHZjaSBxbQCkPYTziACyD5dRwBMNbUUYAiAQkmUAHXcrz0hZH8BbaJU4vgtrAY8it1oGY/j/atpkpA==","keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U"}],"unpackedSize":101050},"main":"dist/index.js","type":"module","types":"./dist/index.d.ts","exports":{".":{"import":"./dist/index.js","require":"./dist/index.cjs"},"./*":{"import":"./dist/*.js","require":"./dist/*.cjs"},"./css":"./dist/index.css","./min/*":{"import":"./dist/*.min.js","require":"./dist/*.min.cjs"},"./min/css":"./dist/index.min.css"},"gitHead":"717d1c7f8dc21ca78953506396ff0d0c4597e2d3","scripts":{"toc":"markdown-toc --maxdepth 3 -i README.md","lint":"eslint \"./**/*.{ts,js}\"","test":"esbuild --bundle ./test/index.ts | tapout","build":"mkdir -p ./dist && rm -rf ./dist/* && npm run build-cjs && npm run build-esm && npm run build-esm:min && npm run build-css && npm run build-css:min","start":"vite","version":"npm run toc && auto-changelog -p --template keepachangelog --breaking-pattern 'BREAKING CHANGE:' && git add CHANGELOG.md README.md","build-cjs":"esbuild src/*.ts --format=cjs --keep-names --tsconfig=tsconfig.build.json --outdir=./dist --out-extension:.js=.cjs --sourcemap","build-css":"npx lightningcss --nesting src/index.css -o dist/index.css","build-esm":"esbuild src/*.ts --format=esm --metafile=dist/meta.json --keep-names --tsconfig=tsconfig.build.json --outdir=./dist --sourcemap && tsc --emitDeclarationOnly --project tsconfig.build.json --outDir dist","build-docs":"typedoc --tsconfig tsconfig.build.json ./src/index.ts","preversion":"npm run lint && stylelint src/*.css","postversion":"git push --follow-tags && npm publish","build-css:min":"npx lightningcss --nesting --minify src/index.css -o dist/index.min.css","build-esm:min":"esbuild ./src/*.ts --format=esm --keep-names --bundle --tsconfig=tsconfig.build.json --minify --out-extension:.js=.min.js --outdir=./dist --sourcemap","build-example":"mkdir -p ./public && rm -rf ./public/* && NODE_ENV=development vite --mode development --base=\"/character-counter\" build","prepublishOnly":"npm run build"},"_npmUser":{"name":"nichoth","email":"nichoth@gmail.com"},"_npmVersion":"11.6.1","description":"[![tests](https://img.shields.io/github/actions/workflow/status/bskyprism/character-counter/nodejs.yml?style=flat-square)](https://github.com/bskyprism/character-counter/actions/workflows/nodejs.yml) [![types](https://img.shields.io/npm/types/@bskyprism/c","directories":{},"_nodeVersion":"22.18.0","dependencies":{"@substrate-system/web-component":"^0.0.38"},"_hasShrinkwrap":false,"devDependencies":{"vite":"^7.3.0","eslint":"^8.57.0","esbuild":"^0.27.2","typedoc":"^0.28.1","axe-core":"^4.11.1","tap-spec":"^5.0.0","tape-run":"^11.0.0","stylelint":"^16.19.1","typescript":"^5.9.3","browserslist":"^4.28.1","lightningcss":"^1.30.2","markdown-toc":"^1.2.0","auto-changelog":"^2.4.0","lightningcss-cli":"^1.30.2","@substrate-system/dom":"^0.1.13","eslint-config-standard":"^17.1.0","@substrate-system/debug":"^0.9.32","@substrate-system/tapout":"^0.0.33","@substrate-system/tapzero":"^0.10.16","@typescript-eslint/parser":"^8.7.0","stylelint-config-standard":"^39.0.1","@typescript-eslint/eslint-plugin":"^8.7.0"},"_npmOperationalInternal":{"tmp":"tmp/character-counter_0.0.1_1768531576175_0.16417500658973427","host":"s3://npm-registry-packages-npm-production"}},"0.0.2":{"name":"@bskyprism/character-counter","version":"0.0.2","description":"Count characters","type":"module","exports":{".":{"import":"./dist/index.js","require":"./dist/index.cjs"},"./css":"./dist/index.css","./min/css":"./dist/index.min.css","./min/*":{"import":"./dist/*.min.js","require":"./dist/*.min.cjs"},"./*":{"import":"./dist/*.js","require":"./dist/*.cjs"}},"main":"dist/index.js","types":"./dist/index.d.ts","directories":{"example":"example","test":"test"},"scripts":{"lint":"eslint \"./**/*.{ts,js}\"","test":"esbuild --bundle ./test/index.ts | tapout","build-css":"npx lightningcss --nesting src/index.css -o dist/index.css","build-css:min":"npx lightningcss --nesting --minify src/index.css -o dist/index.min.css","build-cjs":"esbuild src/*.ts --format=cjs --keep-names --tsconfig=tsconfig.build.json --outdir=./dist --out-extension:.js=.cjs --sourcemap","build-esm":"esbuild src/*.ts --format=esm --metafile=dist/meta.json --keep-names --tsconfig=tsconfig.build.json --outdir=./dist --sourcemap && tsc --emitDeclarationOnly --project tsconfig.build.json --outDir dist","build-esm:min":"esbuild ./src/*.ts --format=esm --keep-names --bundle --tsconfig=tsconfig.build.json --minify --out-extension:.js=.min.js --outdir=./dist --sourcemap","build-example":"mkdir -p ./public && rm -rf ./public/* && NODE_ENV=development vite --mode development --base=\"/character-counter\" build","build-docs":"typedoc --tsconfig tsconfig.build.json ./src/index.ts","build":"mkdir -p ./dist && rm -rf ./dist/* && npm run build-cjs && npm run build-esm && npm run build-esm:min && npm run build-css && npm run build-css:min","start":"vite","toc":"markdown-toc --maxdepth 3 -i README.md","preversion":"npm run lint && stylelint src/*.css","version":"npm run toc && auto-changelog -p --template keepachangelog --breaking-pattern 'BREAKING CHANGE:' && git add CHANGELOG.md README.md","postversion":"git push --follow-tags && npm publish","prepublishOnly":"npm run build"},"dependencies":{"@substrate-system/web-component":"^0.0.38"},"devDependencies":{"@substrate-system/debug":"^0.9.32","@substrate-system/dom":"^0.1.13","@substrate-system/tapout":"^0.0.33","@substrate-system/tapzero":"^0.10.16","@typescript-eslint/eslint-plugin":"^8.7.0","@typescript-eslint/parser":"^8.7.0","auto-changelog":"^2.4.0","axe-core":"^4.11.1","browserslist":"^4.28.1","esbuild":"^0.27.2","eslint":"^8.57.0","eslint-config-standard":"^17.1.0","lightningcss":"^1.30.2","lightningcss-cli":"^1.30.2","markdown-toc":"^1.2.0","stylelint":"^16.19.1","stylelint-config-standard":"^39.0.1","tap-spec":"^5.0.0","tape-run":"^11.0.0","typedoc":"^0.28.1","typescript":"^5.9.3","vite":"^7.3.0"},"keywords":["count","character","web","component"],"homepage":"https://github.com/bskyprism/character-counter#readme","bugs":{"url":"https://github.com/bskyprism/character-counter/issues"},"repository":{"type":"git","url":"git+https://github.com/bskyprism/character-counter.git"},"license":"SEE LICENSE IN LICENSE","author":{"name":"nichoth","email":"nichoth@nichoth.com","url":"https://nichoth.com"},"gitHead":"9db0b46c1fe9b40d1432e708f5f2d5ef217ea0f8","_id":"@bskyprism/character-counter@0.0.2","_nodeVersion":"22.18.0","_npmVersion":"11.6.1","dist":{"integrity":"sha512-bi2ttdKoY1DApsef5vxRHgyi5BC3DEAAAI5nU17rHz/S5lWx8AvqW+7YURZY/OejBkGrYmfV7x8MRd8rDlyg+g==","shasum":"998e4491f34503993c1aaa79e9eadabf40812c71","tarball":"https://registry.npmjs.org/@bskyprism/character-counter/-/character-counter-0.0.2.tgz","fileCount":14,"unpackedSize":100959,"signatures":[{"keyid":"SHA256:DhQ8wR5APBvFHLF/+Tc+AYvPOdTpcIDqOhxsBHRwC7U","sig":"MEUCIHaIEL/InVnt77YowULEFuEzdBN3v5bR+8iGUvwf1Ky7AiEAjFWyjaHvjSZErKn+PoEc6DVn3EIa1i35ZiAyT29K+Zg="}]},"_npmUser":{"name":"nichoth","email":"nichoth@gmail.com"},"maintainers":[{"name":"nichoth","email":"nichoth@gmail.com"}],"_npmOperationalInternal":{"host":"s3://npm-registry-packages-npm-production","tmp":"tmp/character-counter_0.0.2_1768531982685_0.3301599457566873"},"_hasShrinkwrap":false}},"time":{"created":"2026-01-16T02:45:40.265Z","modified":"2026-01-16T02:53:02.954Z","0.0.0":"2026-01-16T02:45:40.504Z","0.0.1":"2026-01-16T02:46:16.313Z","0.0.2":"2026-01-16T02:53:02.832Z"},"author":{"name":"nichoth","email":"nichoth@nichoth.com","url":"https://nichoth.com"},"license":"SEE LICENSE IN LICENSE","description":"Count characters","maintainers":[{"name":"nichoth","email":"nichoth@gmail.com"}],"readme":"# Character Counter\n[![tests](https://img.shields.io/github/actions/workflow/status/bskyprism/character-counter/nodejs.yml?style=flat-square)](https://github.com/bskyprism/character-counter/actions/workflows/nodejs.yml)\n[![types](https://img.shields.io/npm/types/@bskyprism/character-counter?style=flat-square)](README.md)\n[![module](https://img.shields.io/badge/module-ESM%2FCJS-blue?style=flat-square)](README.md)\n[![install size](https://flat.badgen.net/packagephobia/install/@bskyprism/character-counter?cache-control=no-cache)](https://packagephobia.com/result?p=@bskyprism/character-counter)\n[![gzip size](https://img.shields.io/bundlephobia/minzip/@bskyprism/character-counter?style=flat-square)](https://bundlephobia.com/package/@bskyprism/character-counter)\n[![semantic versioning](https://img.shields.io/badge/semver-2.0.0-blue?logo=semver&style=flat-square)](https://semver.org/)\n[![Common Changelog](https://nichoth.github.io/badge/common-changelog.svg)](./CHANGELOG.md)\n[![license](https://img.shields.io/badge/license-Big_Time-blue?style=flat-square)](LICENSE)\n\nA visual character counter web component with a circular progress indicator,\ninspired by Bluesky's post composer.\n\n[See a live demo](https://bskyprism.github.io/character-counter/)\n\n\n## _Featuring_\n\n- **[No Shadow DOM](https://gomakethings.com/podcast/the-shadow-dom-is-an-anti-pattern/)**\n- **Accessible** - ARIA labels and screen reader support\n\n<!-- toc -->\n\n- [Install](#install)\n- [Use](#use)\n  * [Basic Example](#basic-example)\n  * [Dynamic Updates](#dynamic-updates)\n- [Attributes](#attributes)\n  * [Display Behavior](#display-behavior)\n- [Data Attributes](#data-attributes)\n- [CSS Custom Properties](#css-custom-properties)\n  * [Change size globally](#change-size-globally)\n  * [Change colors](#change-colors)\n- [Behavior](#behavior)\n- [API](#api)\n  * [ESM](#esm)\n  * [Common JS](#common-js)\n  * [TypeScript](#typescript)\n- [Pre-Built Files](#pre-built-files)\n  * [Copy Files](#copy-files)\n  * [Use in HTML](#use-in-html-1)\n\n<!-- tocstop -->\n\n## Install\n\n```sh\nnpm i -S @bskyprism/character-counter\n```\n\n## Use\n\n### Basic Example\n\nThis calls the global function `customElements.define`. Just import,\nthen use the tag in your HTML.\n\n#### Import JavaScript\n\n```js\nimport '@bskyprism/character-counter'\n```\n\n#### Import CSS\n\n```js\nimport '@bskyprism/character-counter/css'\n```\n\nOr minified:\n```js\nimport '@bskyprism/character-counter/min/css'\n```\n\n#### Use in HTML\n\n```html\n<character-counter max=\"300\" count=\"50\"></character-counter>\n```\n\n### Dynamic Updates\n\nUpdate the `count` attribute as the user types:\n\n```js\nconst textarea = document.querySelector('textarea')\nconst counter = document.querySelector('character-counter')\n\ntextarea.addEventListener('input', () => {\n    counter.setAttribute('count', textarea.value.length)\n})\n```\n\nOr use the setter:\n\n```js\ncounter.count = textarea.value.length\n```\n\n## Attributes\n\n| Attribute | Type | Default | Description |\n|-----------|------|---------|-------------|\n| `max` | number | `300` | Maximum character count |\n| `count` | number | `0` | Current character count |\n| `hide-count` | boolean | `false` | When present, never shows the remaining count text |\n| `warn` | boolean | `false` | When present, only shows count when within 20 of limit (ignored if `hide-count` is present) |\n\n### Display Behavior\n\nThe component's text display behavior depends on these attributes:\n\n- **Default** (no attributes): Always shows the remaining count\n- **With `warn`**: Only shows count when 20 or fewer characters remain\n- **With `hide-count`**: Never shows the count (takes precedence over `warn`)\n\n```html\n<!-- Always show count -->\n<character-counter max=\"300\" count=\"50\"></character-counter>\n\n<!-- Only show count when near limit -->\n<character-counter max=\"300\" count=\"285\" warn></character-counter>\n\n<!-- Never show count -->\n<character-counter max=\"300\" count=\"50\" hide-count></character-counter>\n```\n\n## Data Attributes\n\nSets data attributes based on state:\n\n| Attribute | Description |\n|-----------|-------------|\n| `data-over-limit` | Present when `count` exceeds `max` |\n| `data-hide-count` | Present when count text should be hidden (uses `visibility: hidden` to prevent layout shift) |\n\n\n## CSS Custom Properties\n\nCustomize the appearance using CSS variables:\n\n| Property | Default | Description |\n|----------|---------|-------------|\n| `--counter-diameter` | `2rem` | Circle diameter (supports any CSS unit) |\n| `--counter-stroke-width` | `4` | Circle stroke width in pixels |\n| `--counter-track-color` | `#e0e0e0` | Background ring color |\n| `--counter-normal-color` | `#1d9bf0` | Progress color when under limit |\n| `--counter-warning-color` | `#f4212e` | Progress color when over limit |\n| `--counter-text-color` | `#536471` | Remaining count text color |\n\n### Change size globally\n\n```css\ncharacter-counter {\n    --counter-diameter: 3rem;\n    --counter-stroke-width: 5;\n}\n```\n\n### Change colors\n\n```css\ncharacter-counter {\n    --counter-normal-color: #059669;\n    --counter-warning-color: #dc2626;\n    --counter-track-color: #f3f4f6;\n}\n```\n\n#### Per-instance customization\n```html\n<character-counter\n    max=\"280\"\n    count=\"0\"\n    style=\"--counter-diameter: 32px; --counter-normal-color: purple\"\n>\n</character-counter>\n```\n\n## Behavior\n\n- **Progress indicator**: The circular ring fills as `count` approaches `max`\n- **Number display**: The remaining count appears to the left of the circle (controlled by `hide-count` and `warn` attributes)\n- **Over-limit state**: When `count` > `max`, the component turns red and shows a negative number (if count display is enabled)\n- **Accessibility**: Announces character count to screen readers via ARIA live regions\n\n## API\n\nThis exposes ESM and common JS via\n[package.json `exports` field](https://nodejs.org/api/packages.html#exports).\n\n### ESM\n```js\nimport '@bskyprism/character-counter'\n// Named import\nimport { CharacterCounter } from '@bskyprism/character-counter'\n```\n\n### Common JS\n\n```js\nrequire('@bskyprism/character-counter')\n```\n\n### TypeScript\n\nThe component includes TypeScript definitions and extends the\nglobal `HTMLElementTagNameMap`.\n\n```ts\n// Type-safe querySelector\nconst counter = document.querySelector('character-counter')\n// counter is typed as CharacterCounter | null\n\n// Set count via property\ncounter.count = 42\n\n// Get computed values\nconsole.log(counter.remaining)      // number\nconsole.log(counter.isOverLimit)    // boolean\nconsole.log(counter.hideCount)      // boolean\nconsole.log(counter.warn)           // boolean\nconsole.log(counter.shouldShowCount) // boolean\n```\n\n## Pre-Built Files\n\nThis package exposes minified JS and CSS files.\nCopy them to a location accessible to your web server,\nthen link to them in HTML.\n\n### Copy Files\n```sh\ncp ./node_modules/@bskyprism/character-counter/dist/index.min.js ./public/character-counter.min.js\ncp ./node_modules/@bskyprism/character-counter/dist/style.min.css ./public/character-counter.css\n```\n\n### Use in HTML\n```html\n<head>\n    <link rel=\"stylesheet\" href=\"./character-counter.css\">\n</head>\n<body>\n    <character-counter max=\"300\" count=\"0\"></character-counter>\n\n    <script type=\"module\" src=\"./character-counter.min.js\"></script>\n</body>\n```\n","readmeFilename":"README.md","homepage":"https://github.com/bskyprism/character-counter#readme","keywords":["count","character","web","component"],"repository":{"type":"git","url":"git+https://github.com/bskyprism/character-counter.git"},"bugs":{"url":"https://github.com/bskyprism/character-counter/issues"}}