npm package: @jagreehal/smart-filters
Description: AI-powered web component for natural language filter control using Chrome's built-in Browser AI
Author: Jag Reehal
Latest version: 1.0.12

# Smart Filters Web Component

> **Progressive Enhancement:** AI features require Chrome 138+ with Prompt API. On unsupported browsers, AI features are disabled but **Saved Filters** always work - allowing users to save and restore filter configurations.

A reusable web component that scans page filters and sets them via natural language (AI) or saved presets. Uses Chrome's built-in Browser AI (Prompt API) to interpret user queries, with deterministic saved filters that work on any browser.

## Features

- **Zero-Config** - Just add the script and component tag, auto-detects all filters on page
- **Saved Filters** - Save and restore filter configurations with custom names (works on all browsers)
- **Progressive Enhancement** - AI features disabled gracefully when unavailable; saved filters always work
- **Floating Mode** - Unobtrusive FAB that expands to full UI (default for zero-config)
- **Bookmarklet Support** - Inject into any website via browser bookmark
- **Automatic Page Scanning** - Detects checkboxes, radio buttons, sliders, dropdowns, and text inputs
- **Natural Language Processing** - Users describe what they want in plain English (AI-powered)
- **Voice Input** - Optional speech recognition support (AI-powered)
- **Customizable Themes** - Multiple color themes (indigo, blue, purple, green, red, slate) + JSON themes
- **CSS Parts & Variables** - Full styling control via `::part()` and CSS custom properties
- **Content Slots** - Inject custom content into predefined locations
- **Responsive Container Queries** - Automatically adapts to narrow containers
- **Variants** - Three UI complexity levels: minimal, compact, full
- **Flexible Positioning** - Floating, fixed corners, or inline
- **Multi-language** - Built-in support for English, Spanish, French, German, Japanese
- **Framework Agnostic** - Works with any HTML page, no dependencies

## Installation

### Option 1: CDN - Auto-Inject (Simplest)

Just include the script from jsDelivr - no element needed:

```html
<script src="https://cdn.jsdelivr.net/npm/@jagreehal/smart-filters/dist/smart-filters-auto.iife.js"></script>
```

Or UNPKG:

```html
<script src="https://unpkg.com/@jagreehal/smart-filters/dist/smart-filters-auto.iife.js"></script>
```

That's it! Automatically injects floating UI and scans the page for filters.

### Option 2: CDN - Configurable Element

Add the script and customize with attributes:

```html
<script type="module" src="https://cdn.jsdelivr.net/npm/@jagreehal/smart-filters"></script>
<smart-filters theme="blue" variant="compact"></smart-filters>
```

Same as auto-inject, but gives you control over positioning and attributes.

### Option 3: Targeted Container

Point to a specific filter container:

```html
<script type="module" src="dist/smart-filters.js"></script>

<div id="my-filters">
  <input type="checkbox" name="wifi" /> Free WiFi
  <input type="range" name="price" min="0" max="500" />
</div>

<smart-filters target="my-filters" position="inline"></smart-filters>
```

### Option 4: NPM

```bash
npm install @jagreehal/smart-filters
```

```javascript
import '@jagreehal/smart-filters';
```

### Option 5: Bookmarklet

Inject Smart Filters into **any website** via bookmarklet:

Create a bookmark with this URL:

```javascript
javascript:(function(){var s=document.createElement('script');s.src='https://cdn.jsdelivr.net/npm/@jagreehal/smart-filters/dist/smart-filters-bookmarklet.iife.js';document.head.appendChild(s)})();
```

Click the bookmark on any page - click again to toggle visibility.

## Build Outputs

| File | Description | Use Case |
|------|-------------|----------|
| `smart-filters.js` | ES module | Modern bundlers, `type="module"` |
| `smart-filters.umd.cjs` | UMD bundle | Legacy bundlers, `<script>` tag |
| `smart-filters-auto.js` | ES module auto-inject | Modern auto-inject |
| `smart-filters-auto.iife.js` | IIFE auto-inject | Universal auto-inject |
| `smart-filters-bookmarklet.iife.js` | IIFE bookmarklet | Browser bookmark injection |

## Usage

### Basic Example

```html
<smart-filters target="my-filters"></smart-filters>

<div id="my-filters">
  <label>
    <input type="checkbox" name="wifi" /> Free WiFi
  </label>
  <label>
    <input type="checkbox" name="parking" /> Parking
  </label>
  <input type="range" name="price" min="0" max="500" value="250" />
</div>
```

### Full-Featured Example

```html
<smart-filters
  target="property-filters"
  position="inline"
  theme="blue"
  variant="full"
  lang="en"
  system-prompt="You are a hotel booking assistant"
  storage-key="hotel-demo"
  auto-examples
  show-saved-filters
  show-voice="true"
  apply-text="Search"
  thinking-text="Searching..."
  debounce="500"
  width="400px"
></smart-filters>
```

### Minimal Variant

```html
<smart-filters
  target="filters"
  variant="minimal"
  theme="slate"
></smart-filters>
```

## Attributes

### Core Attributes

| Attribute | Type | Default | Description |
|-----------|------|---------|-------------|
| `target` | string | - | Selector for filter container. If omitted, auto-detects all filters on page |
| `position` | string | `"floating"` / `"inline"` | Position: `floating` (default when no target), `inline` (default with target), `top-left`, `top-right`, `bottom-left`, `bottom-right` |
| `theme` | string | `"slate"` | Color theme: `slate`, `indigo`, `blue`, `purple`, `green`, `red`, or JSON object |
| `variant` | string | `"full"` | UI complexity: `minimal`, `compact`, `full` |
| `lang` | string | auto | Language: `en`, `es`, `fr`, `de` (auto-detects from browser) |
| `system-prompt` | string | - | Custom AI context/instructions |
| `storage-key` | string | - | localStorage key for persistence (enables saved filters) |
| `auto-examples` | boolean | `false` | AI-generates example queries based on filters |
| `show-saved-filters` | boolean | `false` | Show saved filters UI (requires `storage-key`) |
| `show-voice` | string | `"true"` | Show/hide voice input button |
| `apply-text` | string | - | Custom text for the apply button (overrides locale) |
| `thinking-text` | string | - | Custom text shown beside spinner during processing |
| `debounce` | number | - | Auto-submit delay in ms (e.g., `"500"` triggers after 500ms of inactivity) |
| `debug` | boolean | `false` | Show verbose debug info in UI |
| `width` | string | `"auto"` | CSS width (e.g., `"400px"`, `"100%"`) |
| `z-index` | string | `"1000"` | CSS z-index for fixed positioning |

### Data Attributes for Filter Elements

These attributes can be added to your filter inputs for advanced control:

| Attribute | Purpose | Example |
|-----------|---------|---------|
| `data-filter-key` | Stable key for AI matching (preferred over `name`/`id`) | `data-filter-key="amenity_wifi"` |
| `data-format` | Format string for range display values | `data-format="{value} mi"` |
| `data-value-map` | JSON object mapping values to display labels | `data-value-map='{"1":"$","2":"$$","3":"$$$"}'` |
| `data-for` | Associate a display element with an input | `data-for="mySlider"` |

**Example with data attributes:**

```html
<input
  type="range"
  name="distance"
  data-filter-key="max_distance"
  min="1"
  max="25"
  value="10"
/>
<span id="distance-value" data-format="{value} miles">10 miles</span>
```

### Text Override Attributes

Override any displayed text without changing locale:

| Attribute | Description |
|-----------|-------------|
| `title-text` | Override title |
| `badge-text` | Override badge (set to empty string to hide) |
| `placeholder-text` | Override placeholder |
| `description-text` | Override description |
| `reset-text` | Override reset button text |

### Icon Customization

| Attribute | Description |
|-----------|-------------|
| `icon-url` | URL to custom icon image (replaces default SVG) |

### Callback Attributes

Specify window function names to call on events:

| Attribute | Description |
|-----------|-------------|
| `on-applied` | Called when filters applied successfully |
| `on-error` | Called on error |
| `on-loading` | Called when processing starts |
| `on-ready` | Called when component is ready (includes AI availability status) |
| `on-reset` | Called when filters are reset |
| `on-filter-saved` | Called when a filter preset is saved |
| `on-filter-loaded` | Called when a filter preset is loaded |
| `on-filter-deleted` | Called when a filter preset is deleted |

```html
<smart-filters
  target="filters"
  on-applied="handleFiltersApplied"
  on-error="handleError"
></smart-filters>

<script>
  function handleFiltersApplied(detail) {
    console.log('Filters applied:', detail.query, detail.actions);
  }
  function handleError(detail) {
    console.error('Error:', detail.error);
  }
</script>
```

## Events

The component dispatches custom events for integration:

| Event | When | Detail |
|-------|------|--------|
| `smartfilters:applied` | Filters successfully applied | `{ query, actions }` |
| `smartfilters:error` | Error occurred | `{ error, query, rawResponse? }` |
| `smartfilters:loading` | Processing starts | `{ query }` |
| `smartfilters:ready` | Component ready | `{ available: boolean }` (AI availability) |
| `smartfilters:reset` | Filters reset | `{ filters }` |
| `smartfilters:voicestart` | Voice input begins | `{ active: true }` |
| `smartfilters:voiceend` | Voice input ends | `{ active: false }` |
| `smartfilters:filtersaved` | Filter preset saved | `{ id, name, state }` |
| `smartfilters:filterloaded` | Filter preset loaded | `{ id, name }` |
| `smartfilters:filterdeleted` | Filter preset deleted | `{ id, name }` |

```javascript
const sf = document.querySelector('smart-filters');

sf.addEventListener('smartfilters:applied', (e) => {
  console.log('Query:', e.detail.query);
  console.log('Actions:', e.detail.actions);
});

sf.addEventListener('smartfilters:loading', (e) => {
  showSpinner();
});

sf.addEventListener('smartfilters:ready', () => {
  console.log('AI is ready');
});
```

## Public Methods

```javascript
const sf = document.querySelector('smart-filters');

// Apply a query programmatically
await sf.applyQuery('Show pet-friendly hotels under $200');

// Reset all filters
sf.resetFilters();

// Get current filter values
const state = sf.getFilterState();
// { wifi: true, parking: false, price: '150', ... }

// Set filter values programmatically
sf.setFilterState({ wifi: true, price: '200' });

// Apply actions directly (no AI)
sf.applyActions([
  { key: 'wifi', action: 'check', value: 'true' },
  { key: 'price', action: 'set', value: '200' }
]);

// Saved filters (works without AI)
const saved = sf.getSavedFilters();
// [{ id: 'sf-123', name: 'Beach Hotels', state: {...}, createdAt: 1234567890 }]

// Save current filter state
const filter = sf.saveCurrentFilters('Beach Hotels');

// Load a saved filter by ID
sf.loadSavedFilter('sf-123');

// Delete a saved filter
sf.deleteSavedFilter('sf-123');

// Clear all saved filters
sf.clearSavedFilters();

// Get all detected filters
const filters = sf.getFilters();

// Check if AI is available
if (sf.isAIAvailable) {
  // AI features available (query input, auto-examples, voice)
} else {
  // Only saved filters work
}
```

## Slots

Inject custom content into predefined locations:

```html
<smart-filters target="filters">
  <span slot="header-start">
    <img src="logo.png" alt="Logo" width="24" />
  </span>
  <span slot="header-end">
    <button>Settings</button>
  </span>
  <div slot="before-input">
    <p>Search tip: Try "pet-friendly under $200"</p>
  </div>
  <div slot="after-input">
    <small>Powered by AI</small>
  </div>
  <div slot="footer">
    <a href="/help">Need help?</a>
  </div>
</smart-filters>
```

| Slot Name | Location |
|-----------|----------|
| `header-start` | Before icon/title in header |
| `header-end` | After badge, before status |
| `before-input` | Above input textarea |
| `after-input` | Below input, above examples |
| `footer` | After reset button |

## CSS Parts

Style component internals using `::part()`:

```css
smart-filters::part(container) {
  border: 2px solid #000;
}

smart-filters::part(button) {
  background: #0066cc;
  border-radius: 20px;
}

smart-filters::part(input) {
  font-size: 16px;
}
```

| Part Name | Element |
|-----------|---------|
| `container` | Main wrapper (`.sf`) |
| `header` | Header row |
| `title` | Title text |
| `badge` | AI badge |
| `status` | Status indicator |
| `description` | Description text |
| `input` | Main textarea |
| `button` | Apply button |
| `voice-button` | Voice input button |
| `examples` | Examples container |
| `example` | Example chip |
| `log` | Actions log |
| `reset` | Reset button |
| `icon` | Custom icon (when using `icon-url`) |
| `row` | Input row container |
| `fab` | Floating action button (floating mode) |
| `panel` | Expandable panel (floating mode) |
| `close` | Close button in panel header (floating mode) |

## CSS Variables

### Typography

```css
smart-filters {
  --sf-font: system-ui, sans-serif;
  --sf-font-size: 14px;
  --sf-font-weight: 400;
  --sf-font-weight-medium: 500;
  --sf-font-weight-bold: 600;
}
```

### Spacing

```css
smart-filters {
  --sf-space-xs: 0.25rem;
  --sf-space-sm: 0.5rem;
  --sf-space-md: 0.75rem;
  --sf-space-lg: 1rem;
  --sf-space-xl: 1.5rem;
  --sf-gap: 0.5rem;
  --sf-padding: 1rem;
}
```

### Border Radius

```css
smart-filters {
  --sf-radius: 0.75rem;
  --sf-radius-sm: 0.5rem;
  --sf-radius-full: 9999px;
}
```

### Shadows

```css
smart-filters {
  --sf-shadow: 0 4px 6px -1px rgba(0,0,0,0.1);
  --sf-shadow-lg: 0 10px 25px -5px rgba(0,0,0,0.15);
}
```

### Colors (Base)

```css
smart-filters {
  --sf-bg: #ffffff;
  --sf-bg-subtle: #fafafa;
  --sf-border: #e5e5e5;
  --sf-text: #171717;
  --sf-text-muted: #737373;
}
```

### Colors (Interactive)

```css
smart-filters {
  --sf-primary: #1a56db;
  --sf-primary-hover: #1e40af;
  --sf-primary-subtle: #f0f4ff;
  --sf-primary-border: #e0e7ff;
}
```

### Colors (States)

```css
smart-filters {
  --sf-success: #22c55e;
  --sf-success-subtle: #dcfce7;
  --sf-success-border: #86efac;
  --sf-success-text: #166534;
  --sf-error: #ef4444;
  --sf-error-subtle: #fee2e2;
  --sf-error-border: #fca5a5;
  --sf-error-text: #991b1b;
}
```

### Animation

```css
smart-filters {
  --sf-duration: 0.15s;
  --sf-duration-slow: 1.5s;
  --sf-duration-fast: 0.7s;
  --sf-easing: ease-out;
}
```

## JSON Themes

For complete custom theming, pass a JSON object to the `theme` attribute:

```html
<smart-filters
  target="filters"
  theme='{"bg":"#1a1a2e","text":"#eee","primary":"#e94560"}'
></smart-filters>
```

Keys can omit the `--sf-` prefix. Available keys:

- `bg`, `bg-subtle` - Backgrounds
- `border` - Border color
- `text`, `text-muted` - Text colors
- `primary`, `primary-hover`, `primary-subtle`, `primary-border` - Primary/accent colors

## Container Queries

The component automatically adapts to narrow containers:

- **< 350px**: Hides badge, status text, description. Smaller font sizes.
- **< 250px**: Hides examples, saved filters. Header wraps.
- **> 500px**: Button can be side-by-side with input.

This makes the component embeddable in sidebars, modals, and dashboard widgets.

## Variants

| Variant | Features | Use Case |
|---------|----------|----------|
| `minimal` | Icon, title, textarea, link-button only | Clean, Booking.com-style integration |
| `compact` | Adds badge, examples (no saved filters/description) | Balanced UI |
| `full` | All features: badge, voice, saved filters, examples, action log | Feature-rich dashboards |

## Supported Filter Types

The component automatically detects and supports:

### Native HTML Inputs

- **Checkboxes** - `input[type="checkbox"]`
- **Radio Buttons** - `input[type="radio"]`
- **Range Sliders** - `input[type="range"]`
- **Number Inputs** - `input[type="number"]`
- **Date Inputs** - `input[type="date"]`
- **Color Pickers** - `input[type="color"]`
- **Select Dropdowns** - `<select>`
- **Multi-Select** - `<select multiple>`
- **Text Inputs** - `input[type="text"]`, `input[type="search"]`
- **Textareas** - `<textarea>`

### ARIA-Based Components (Modern UI Libraries)

Smart Filters automatically detects custom components built with accessible patterns:

- **ARIA Checkboxes** - `role="checkbox"` with `aria-checked` (Radix UI, shadcn)
- **ARIA Switches** - `role="switch"` with `aria-checked` or `data-state` (Headless UI)
- **ARIA Toggle Buttons** - `aria-pressed="true|false"` (GitHub, Figma patterns)
- **ARIA Sliders** - `role="slider"` with `aria-valuemin/max/now` (Booking.com, Airbnb)
- **ARIA Listboxes** - `role="listbox"` with `role="option"` (React Select, Headless UI)
- **Link Checkboxes** - Navigation links styled as checkboxes (Amazon, eBay)

This enables Smart Filters to work seamlessly with:

- **Radix UI** / **shadcn/ui**
- **Headless UI**
- **React Aria**
- **Material-UI** / **MUI**
- **Ant Design**
- **Chakra UI**
- Any accessible custom component

### Auto-Excluded Elements

The following are automatically excluded from scanning:

- Inputs in `<header>`, `<nav>`, or `<footer>`
- Login and sign-in forms
- Password and email fields
- Hidden inputs
- Search boxes (with `role="search"`)
- Smart Filters' own inputs

## Example Queries

Users can type natural language queries like:

- "Pet-friendly hotels under $150 with pool"
- "4+ star rating, free cancellation, breakfast included"
- "Remote jobs paying $100k+ with stock options"
- "Direct flights under $500, morning departure"

## Accessibility

Smart Filters is built with accessibility in mind:

### Keyboard Navigation

- **Tab**: Navigate between interactive elements
- **Enter/Space**: Activate buttons, select examples
- **Arrow Up/Down**: Navigate saved filters list
- **Escape**: Close saved filters list, cancel voice input
- **Enter** (in filter name input): Save current filter

### Screen Reader Support

- ARIA live regions announce state changes (loading, success, error, reset)
- Proper ARIA labels on all interactive elements
- Role attributes for semantic structure (`region`, `listbox`, `status`)
- Focus management in dropdown menus

### Visual Accessibility

- **Focus indicators**: Visible 2px outline on all focusable elements
- **Reduced motion**: Respects `prefers-reduced-motion` - disables all animations
- **High contrast**: Full support for Windows High Contrast Mode (`forced-colors`)
- **Increased contrast**: Enhanced borders when `prefers-contrast: more` is set
- **Color independence**: Status conveyed through text and shape, not just color

### Testing Recommendations

```bash
# Test with screen reader
# macOS: VoiceOver (Cmd + F5)
# Windows: NVDA or JAWS

# Test keyboard navigation
# Tab through all elements without mouse

# Test reduced motion
# Enable in system preferences or use browser devtools

# Test high contrast
# Windows: Settings > Ease of Access > High Contrast
```

## Requirements

- **Chrome 138+** with built-in AI enabled (for AI features)
- **Prompt API** must be available (for AI features)
- **Any modern browser** (for saved filters - progressive enhancement)

## Browser Compatibility

| Feature | Chrome 138+ | Other Browsers |
|---------|-------------|----------------|
| Saved Filters | ✅ | ✅ |
| AI Query Input | ✅ | ❌ (disabled) |
| Voice Input | ✅ | ❌ (hidden) |
| Auto-Examples | ✅ | ❌ (hidden) |

The component uses **progressive enhancement** - saved filters always work, AI features are disabled gracefully when unavailable.

## License

MIT License