cookie-nom
v0.1.0
Published
A calm, neuro-inclusive cookie-consent framework with LCARS-inspired UI, automatic cookie detection, and script blocking
Maintainers
Readme
CookieNom
A calm, neuro-inclusive cookie-consent framework
CookieNom replaces intrusive cookie banners with a friendly, LCARS-style "CookieCar" that sits quietly at the bottom of the page. It handles consent, auto-detects and categorises cookies, blocks third-party scripts until consent is given, and lets users manage preferences at any time -- no pop-ups, no dark patterns.
Features
- Neuro-inclusive & Accessible -- ARIA labels, keyboard navigation, focus trapping,
prefers-reduced-motionsupport - LCARS-Inspired Design -- retro-futuristic styling, soft animations, rounded shapes
- Privacy-First -- defaults to essential-only; "no action" = no consent
- Automatic Cookie Detection -- built-in database of 40+ known cookie patterns (GA, Facebook, Hotjar, TikTok, etc.)
- Script Blocking -- block third-party
<script>tags until the matching category is consented to - Cookie Cleanup -- removes non-consented cookies when consent is revoked
- Granular Preferences -- per-category toggle dialog with detected cookie details
- Emoji Status Indicator -- persistent button showing current state
- Zero Dependencies -- lightweight, no runtime deps
- TypeScript -- full type definitions included
Installation
npm install cookie-nomQuick Start
import CookieNom from 'cookie-nom';
const cookieNom = new CookieNom({
onConsentChange: (state, categories) => {
console.log('Consent changed:', state, categories);
}
});
cookieNom.init();That's it. CookieNom will show a consent banner for first-time visitors and a small indicator for returning visitors.
Script Blocking
Tag third-party scripts with type="text/plain" and data-cookienom="<category>". CookieNom will only execute them once the matching category is consented to.
<!-- Blocked until analytics consent -->
<script type="text/plain" data-cookienom="analytics"
src="https://www.googletagmanager.com/gtag/js?id=G-XXXXX"></script>
<!-- Blocked until marketing consent -->
<script type="text/plain" data-cookienom="marketing">
fbq('init', '1234567890');
fbq('track', 'PageView');
</script>When consent is granted, CookieNom replaces the blocked script with an executable copy, preserving all attributes. It also watches for dynamically added scripts via MutationObserver.
Cookie Detection
CookieNom includes a built-in database of common cookie patterns:
| Category | Examples |
|----------|----------|
| Analytics | _ga, _gid, _hj*, mp_*, _pk_*, _clck |
| Marketing | _fbp, _gcl_*, _ttp, _uet*, IDE, li_sugr |
| Preferences | i18next, lang, locale, wp-settings-* |
You can add custom patterns:
import { CookieCategory } from 'cookie-nom';
cookieNom.addCookiePattern(/^myapp_track/, CookieCategory.ANALYTICS, 'MyApp Tracking');Scan all cookies on the current page:
const cookies = cookieNom.scanCookies();
// [{ name: '_ga', category: 'analytics', service: 'Google Analytics', value: '...' }, ...]Configuration
const cookieNom = new CookieNom({
// Banner message
message: 'We use cookies to improve your experience. Essential cookies are always active.',
// Button labels (equal visual weight -- no dark patterns)
acceptText: 'Accept All', // default
declineText: 'No Thanks', // default
// Position of the CookieCar
position: 'bottom-left', // 'bottom-left' | 'bottom-right' | 'bottom-center'
// Auto-hide delay in ms (auto-hide = implicit decline)
autoHideDelay: 30000, // default: 30s
// Cookie categories to manage
categories: [
CookieCategory.ESSENTIAL,
CookieCategory.ANALYTICS,
CookieCategory.MARKETING,
CookieCategory.PREFERENCES
],
// Name and expiry of the consent cookie itself
cookieName: 'cookienom_consent', // default
cookieExpiry: 365, // days, default
// Consent change callback
onConsentChange: (state, categories) => {
if (categories.includes(CookieCategory.ANALYTICS)) {
// enable analytics
}
},
// Additional CSS to inject
customStyles: ''
});API
Methods
| Method | Description |
|--------|-------------|
| init() | Initialize -- call when DOM is ready |
| isCategoryAllowed(category) | Check if a category is currently allowed |
| getState() | Get current state: 'pending', 'essentials_only', or 'accepted' |
| getAllowedCategories() | Get array of currently allowed categories |
| showPreferences() | Open the preferences dialog programmatically |
| scanCookies() | Scan and categorize all cookies on the page |
| addCookiePattern(pattern, category, service) | Register a custom cookie pattern |
| reset() | Clear consent and re-show the banner |
| destroy() | Remove all UI elements and stop script observation |
Enums
enum ConsentState {
PENDING = 'pending',
ESSENTIALS_ONLY = 'essentials_only',
ACCEPTED = 'accepted'
}
enum CookieCategory {
ESSENTIAL = 'essential',
ANALYTICS = 'analytics',
MARKETING = 'marketing',
PREFERENCES = 'preferences'
}Consent States
| Emoji | State | Meaning |
|-------|-------|---------|
| neutral | PENDING | No decision yet |
| sleeping | ESSENTIALS_ONLY | Declined or auto-dismissed |
| happy | ACCEPTED | Opted in to extras |
Accessibility
role="dialog"witharia-labelledbyandaria-describedby- Keyboard navigation: Tab to cycle, Enter/Space to activate, Escape to decline
- Focus trap in preferences dialog with focus restoration on close
aria-live="polite"for auto-dismiss countdown- Toggle switches use
role="switch"witharia-checked - All animations respect
prefers-reduced-motion: reduce - High-contrast LCARS colour palette
Design Principles
- No dark patterns -- accept and decline have equal visual weight
- Calm & kind -- non-intrusive, auto-hides gently if ignored
- Clear consent -- auto-hide = essentials only (no implicit acceptance)
- Always reversible -- persistent indicator lets users change their mind
- Neuro-inclusive -- low stimulation, soft motion, clear labels
Browser Support
Chrome/Edge 90+, Firefox 88+, Safari 14+, Opera 76+.
Development
npm install
npm run build # compile to dist/
npm run dev # watch mode
npm test # run testsLicense
MIT
Made with care, for humans.
