@passcore/jquery
jQuery plugin
@passcore/jquery adds $.fn.password() to jQuery 3 and 4. Point it at a password input and it appends an accessible meter to the closest container, and fires events as people type.
pnpm add @passcore/jquery jquery
Every meter on this page speaks your language. The playground gives each one the texts of the language you pick at the top, and its accessible name. To keep the snippets short, only the translation demos show how; the show and hide button in the fields belongs to the playground, not to the package.
Default
Out of the box, the meter waits out of sight until the field gets focus, then slides in. It slides away again if the field is left empty.
import $ from 'jquery'
import '@passcore/jquery'
import '@passcore/jquery/styles.css'
$('#default-password').password()
Requirements checklist
The classic signup form: every condition, red or green as people type. password.score brings the full result, and result.rules lists every enabled rule, in order, with its id, passed and params. The plugin reads the username when the password changes, so its field triggers an update too. Every item stays grey until something is typed: an empty password is “not common” too, and saying so in green would mislead.
Requirements
- At least 8 characters , met , not met
- Not a common password , met , not met
- No personal details, like your username , met , not met
- A lowercase letter , met , not met
- An uppercase letter , met , not met
- A number , met , not met
- A symbol , met , not met
7 requirements to meet
// the list: <li data-rule="minLength" data-state="idle">At least 8 characters</li>, and so on
const $summary = $('#checklist-summary')
$('#signup-password')
.password({
userInputs: ['#signup-username'],
rules: { minLength: 8, lowercase: 1, uppercase: 1, numbers: 1, symbols: 1 },
animate: false,
})
.on('password.score', (event, percent, result) => {
const typed = event.target.value !== ''
for (const rule of result.rules) {
$(`#checklist [data-rule="${rule.id}"]`)
.attr('data-state', !typed ? 'idle' : rule.passed ? 'met' : 'unmet')
}
// a live region: only touch it when the count changes
const met = result.rules.filter((rule) => rule.passed).length
const text = typed ? `${met} of ${result.rules.length} requirements met` : `${result.rules.length} requirements to meet`
if ($summary.text() !== text) {
$summary.text(text)
}
})
// the plugin reads the username when the password changes: update on its changes too
$('#signup-username').on('input', () => $('#signup-password').trigger('input'))
Always visible, with the percent
With animate: false the meter is there from the start, and showPercent adds the number.
$('#always-password').password({
animate: false,
showPercent: true,
})
Linked to a username
With userInputs the password can’t contain the username: try typing it in both fields. The fields are read on every keystroke.
$('#linked-password').password({
userInputs: ['#username'],
showPercent: true,
})
Bundled translations
The bundled files go in translations, and locale picks the plural forms. Switch the language at the top: the plugin reads its texts once, so the playground mounts it again.
import en from '@passcore/jquery/locales/en.json'
import es from '@passcore/jquery/locales/es.json'
import ca from '@passcore/jquery/locales/ca.json'
const locales = { en, es, ca }
$('#translations-password').password({
translations: locales[lang],
locale: lang,
animate: false,
showPercent: true,
})
Translations with i18next
translate takes the t of an i18next instance, here the playground’s own. The plugin only rewrites the message when it changes, so a language change needs a fresh mount.
import i18next from 'i18next'
import es from '@passcore/jquery/locales/es.json'
import ca from '@passcore/jquery/locales/ca.json'
i18next.addResourceBundle('es', 'passcore', es)
i18next.addResourceBundle('ca', 'passcore', ca)
const $input = $('#i18next-password')
const mount = () => $input.password({
translate: (key, params) => i18next.t(key, { ns: 'passcore', ...params }),
animate: false,
showPercent: true,
})
mount()
// the texts are translated on every update: refresh the meter after changing language
i18next.on('languageChanged', () => $input.password('refresh'))
Callbacks and events
password.score fires on every keystroke with the percent and the full result. Here it drives the score and the Send button.
$('#events-password')
.password({ animate: false })
.on('password.score', (event, percent) => {
$('#send').prop('disabled', percent <= 75)
$('#events-score').text(percent + '%')
})
Input group
The meter goes to the closest div by default. With closestSelector: '.form-group' it lands below the whole input group instead.
$('#group-password').password({
animate: false,
closestSelector: '.form-group',
})
Theming
Colours, track and height are CSS custom properties: --pass-color-very-weak to --pass-color-strong, --pass-track and --pass-height. Play with them here; the CSS below follows along.
- Very weak
- Weak
- Fair
- Good
- Strong
.signup {
--pass-height: 6px;
--pass-track: #e4e2db;
--pass-color-very-weak: #d92d20;
--pass-color-weak: #e06a0f;
--pass-color-fair: #c99400;
--pass-color-good: #5e9e0f;
--pass-color-strong: #16884a;
}
The controls only change this preview, so the other demos keep their look. Set the properties on :root to theme a whole site.