@bskyprism/character-counter
v0.0.2
Published
Count characters
Readme
Character Counter
A visual character counter web component with a circular progress indicator, inspired by Bluesky's post composer.
Featuring
- No Shadow DOM
- Accessible - ARIA labels and screen reader support
Install
npm i -S @bskyprism/character-counterUse
Basic Example
This calls the global function customElements.define. Just import,
then use the tag in your HTML.
Import JavaScript
import '@bskyprism/character-counter'Import CSS
import '@bskyprism/character-counter/css'Or minified:
import '@bskyprism/character-counter/min/css'Use in HTML
<character-counter max="300" count="50"></character-counter>Dynamic Updates
Update the count attribute as the user types:
const textarea = document.querySelector('textarea')
const counter = document.querySelector('character-counter')
textarea.addEventListener('input', () => {
counter.setAttribute('count', textarea.value.length)
})Or use the setter:
counter.count = textarea.value.lengthAttributes
| Attribute | Type | Default | Description |
|-----------|------|---------|-------------|
| max | number | 300 | Maximum character count |
| count | number | 0 | Current character count |
| hide-count | boolean | false | When present, never shows the remaining count text |
| warn | boolean | false | When present, only shows count when within 20 of limit (ignored if hide-count is present) |
Display Behavior
The component's text display behavior depends on these attributes:
- Default (no attributes): Always shows the remaining count
- With
warn: Only shows count when 20 or fewer characters remain - With
hide-count: Never shows the count (takes precedence overwarn)
<!-- Always show count -->
<character-counter max="300" count="50"></character-counter>
<!-- Only show count when near limit -->
<character-counter max="300" count="285" warn></character-counter>
<!-- Never show count -->
<character-counter max="300" count="50" hide-count></character-counter>Data Attributes
Sets data attributes based on state:
| Attribute | Description |
|-----------|-------------|
| data-over-limit | Present when count exceeds max |
| data-hide-count | Present when count text should be hidden (uses visibility: hidden to prevent layout shift) |
CSS Custom Properties
Customize the appearance using CSS variables:
| Property | Default | Description |
|----------|---------|-------------|
| --counter-diameter | 2rem | Circle diameter (supports any CSS unit) |
| --counter-stroke-width | 4 | Circle stroke width in pixels |
| --counter-track-color | #e0e0e0 | Background ring color |
| --counter-normal-color | #1d9bf0 | Progress color when under limit |
| --counter-warning-color | #f4212e | Progress color when over limit |
| --counter-text-color | #536471 | Remaining count text color |
Change size globally
character-counter {
--counter-diameter: 3rem;
--counter-stroke-width: 5;
}Change colors
character-counter {
--counter-normal-color: #059669;
--counter-warning-color: #dc2626;
--counter-track-color: #f3f4f6;
}Per-instance customization
<character-counter
max="280"
count="0"
style="--counter-diameter: 32px; --counter-normal-color: purple"
>
</character-counter>Behavior
- Progress indicator: The circular ring fills as
countapproachesmax - Number display: The remaining count appears to the left of the circle (controlled by
hide-countandwarnattributes) - Over-limit state: When
count>max, the component turns red and shows a negative number (if count display is enabled) - Accessibility: Announces character count to screen readers via ARIA live regions
API
This exposes ESM and common JS via
package.json exports field.
ESM
import '@bskyprism/character-counter'
// Named import
import { CharacterCounter } from '@bskyprism/character-counter'Common JS
require('@bskyprism/character-counter')TypeScript
The component includes TypeScript definitions and extends the
global HTMLElementTagNameMap.
// Type-safe querySelector
const counter = document.querySelector('character-counter')
// counter is typed as CharacterCounter | null
// Set count via property
counter.count = 42
// Get computed values
console.log(counter.remaining) // number
console.log(counter.isOverLimit) // boolean
console.log(counter.hideCount) // boolean
console.log(counter.warn) // boolean
console.log(counter.shouldShowCount) // booleanPre-Built Files
This package exposes minified JS and CSS files. Copy them to a location accessible to your web server, then link to them in HTML.
Copy Files
cp ./node_modules/@bskyprism/character-counter/dist/index.min.js ./public/character-counter.min.js
cp ./node_modules/@bskyprism/character-counter/dist/style.min.css ./public/character-counter.cssUse in HTML
<head>
<link rel="stylesheet" href="./character-counter.css">
</head>
<body>
<character-counter max="300" count="0"></character-counter>
<script type="module" src="./character-counter.min.js"></script>
</body>