featuredrop 1.3.0 → 2.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. package/README.md +287 -760
  2. package/dist/adapters.cjs +1757 -0
  3. package/dist/adapters.cjs.map +1 -0
  4. package/dist/adapters.d.cts +744 -0
  5. package/dist/adapters.d.ts +744 -0
  6. package/dist/adapters.js +1745 -0
  7. package/dist/adapters.js.map +1 -0
  8. package/dist/admin.cjs +148 -32
  9. package/dist/admin.cjs.map +1 -1
  10. package/dist/admin.d.cts +14 -3
  11. package/dist/admin.d.ts +14 -3
  12. package/dist/admin.js +148 -32
  13. package/dist/admin.js.map +1 -1
  14. package/dist/bridges.cjs +134 -15
  15. package/dist/bridges.cjs.map +1 -1
  16. package/dist/bridges.d.cts +12 -5
  17. package/dist/bridges.d.ts +12 -5
  18. package/dist/bridges.js +114 -15
  19. package/dist/bridges.js.map +1 -1
  20. package/dist/ci.cjs +34 -0
  21. package/dist/ci.cjs.map +1 -1
  22. package/dist/ci.d.cts +5 -1
  23. package/dist/ci.d.ts +5 -1
  24. package/dist/ci.js +34 -1
  25. package/dist/ci.js.map +1 -1
  26. package/dist/cms.cjs +835 -0
  27. package/dist/cms.cjs.map +1 -0
  28. package/dist/cms.d.cts +236 -0
  29. package/dist/cms.d.ts +236 -0
  30. package/dist/cms.js +829 -0
  31. package/dist/cms.js.map +1 -0
  32. package/dist/featuredrop.cjs +23 -2
  33. package/dist/featuredrop.cjs.map +1 -1
  34. package/dist/flags.cjs +27 -7
  35. package/dist/flags.cjs.map +1 -1
  36. package/dist/flags.d.cts +14 -0
  37. package/dist/flags.d.ts +14 -0
  38. package/dist/flags.js +27 -7
  39. package/dist/flags.js.map +1 -1
  40. package/dist/index.cjs +52 -4460
  41. package/dist/index.cjs.map +1 -1
  42. package/dist/index.d.cts +1 -1340
  43. package/dist/index.d.ts +1 -1340
  44. package/dist/index.js +53 -4387
  45. package/dist/index.js.map +1 -1
  46. package/dist/markdown.cjs +257 -0
  47. package/dist/markdown.cjs.map +1 -0
  48. package/dist/markdown.d.cts +9 -0
  49. package/dist/markdown.d.ts +9 -0
  50. package/dist/markdown.js +234 -0
  51. package/dist/markdown.js.map +1 -0
  52. package/dist/preact.cjs +37 -12
  53. package/dist/preact.cjs.map +1 -1
  54. package/dist/preact.js +17 -12
  55. package/dist/preact.js.map +1 -1
  56. package/dist/react.cjs +37 -12
  57. package/dist/react.cjs.map +1 -1
  58. package/dist/react.js +17 -12
  59. package/dist/react.js.map +1 -1
  60. package/dist/renderer.cjs +503 -0
  61. package/dist/renderer.cjs.map +1 -0
  62. package/dist/renderer.d.cts +250 -0
  63. package/dist/renderer.d.ts +250 -0
  64. package/dist/renderer.js +501 -0
  65. package/dist/renderer.js.map +1 -0
  66. package/dist/rss.cjs +291 -0
  67. package/dist/rss.cjs.map +1 -0
  68. package/dist/rss.d.cts +158 -0
  69. package/dist/rss.d.ts +158 -0
  70. package/dist/rss.js +268 -0
  71. package/dist/rss.js.map +1 -0
  72. package/dist/vue.cjs +23 -2
  73. package/dist/vue.cjs.map +1 -1
  74. package/dist/vue.js +3 -2
  75. package/dist/vue.js.map +1 -1
  76. package/package.json +72 -6
package/README.md CHANGED
@@ -1,904 +1,431 @@
1
1
  <p align="center">
2
- <h1 align="center">featuredrop</h1>
3
- <p align="center">
4
- <strong>"New" badges that actually expire.</strong>
5
- <br />
6
- Lightweight feature discovery for SaaS sidebars, dashboards, and nav menus.
7
- </p>
2
+ <img src="apps/docs/public/branding/icon-256.png" alt="FeatureDrop" width="72" height="72" />
8
3
  </p>
9
4
 
5
+ <h1 align="center">FeatureDrop</h1>
6
+
10
7
  <p align="center">
11
- <a href="https://www.npmjs.com/package/featuredrop"><img src="https://img.shields.io/npm/v/featuredrop?color=f59e0b&label=npm" alt="npm version"></a>
12
- <a href="https://bundlephobia.com/package/featuredrop"><img src="https://img.shields.io/bundlephobia/minzip/featuredrop?color=22c55e&label=size" alt="bundle size"></a>
13
- <a href="https://github.com/GLINCKER/featuredrop/actions/workflows/ci.yml"><img src="https://github.com/GLINCKER/featuredrop/actions/workflows/ci.yml/badge.svg" alt="CI"></a>
14
- <a href="https://github.com/GLINCKER/featuredrop/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/featuredrop?color=blue" alt="license"></a>
8
+ <strong>The open-source product adoption toolkit.</strong><br />
9
+ Changelogs Tours Checklists • Hotspots • Feedback — from your own codebase.<br />
10
+ &lt; 3 kB core &nbsp;·&nbsp; Zero vendor lock-in &nbsp;·&nbsp; MIT licensed
15
11
  </p>
16
12
 
17
13
  <p align="center">
18
- <a href="#quick-start">Quick Start</a> &bull;
19
- <a href="#components">Components</a> &bull;
20
- <a href="#react">React</a> &bull;
21
- <a href="https://github.com/GLINCKER/featuredrop/blob/main/docs/API.md">API Docs</a> &bull;
22
- <a href="https://github.com/GLINCKER/featuredrop/blob/main/docs/ARCHITECTURE.md">Architecture</a>
14
+ <a href="https://github.com/GLINCKER/featuredrop/stargazers"><img src="https://img.shields.io/github/stars/GLINCKER/featuredrop?style=flat&color=f59e0b&label=stars" alt="GitHub stars" /></a>
15
+ <a href="https://www.npmjs.com/package/featuredrop"><img src="https://img.shields.io/npm/v/featuredrop?color=ea580c&label=npm" alt="npm version" /></a>
16
+ <a href="https://www.npmjs.com/package/featuredrop"><img src="https://img.shields.io/npm/dm/featuredrop?color=22c55e&label=downloads" alt="npm downloads" /></a>
17
+ <a href="https://bundlephobia.com/package/featuredrop"><img src="https://img.shields.io/bundlephobia/minzip/featuredrop?color=22c55e&label=gzipped" alt="bundle size" /></a>
18
+ <a href="https://github.com/GLINCKER/featuredrop/blob/main/LICENSE"><img src="https://img.shields.io/npm/l/featuredrop?color=6366f1&label=license" alt="MIT license" /></a>
19
+ <a href="https://featuredrop.dev"><img src="https://img.shields.io/badge/docs-live-ea580c" alt="Live docs" /></a>
23
20
  </p>
24
21
 
25
- ---
22
+ <p align="center">
23
+ <a href="https://featuredrop.dev/docs/quickstart">Quickstart</a> &bull;
24
+ <a href="https://featuredrop.dev/docs/components/gallery">Components</a> &bull;
25
+ <a href="https://featuredrop.dev/playground">Playground</a> &bull;
26
+ <a href="https://featuredrop.dev/docs/api">API Reference</a> &bull;
27
+ <a href="#migration-from-beamer--pendo">Migration Guide</a>
28
+ </p>
26
29
 
27
- ## The Problem
30
+ <p align="center">
31
+ <img src="apps/docs/public/og/github-social.png" alt="FeatureDrop — The open-source product adoption toolkit" width="720" />
32
+ </p>
28
33
 
29
- Every SaaS ships features. Users miss them. You need "New" badges on sidebar items, but:
30
34
 
31
- - **LaunchDarkly / PostHog** — Feature flags, not discovery badges. Overkill.
32
- - **Beamer / Headway** — External widget, vendor lock-in, $59-299/mo.
33
- - **Joyride / Shepherd.js** — Product tours, not persistent badges.
34
- - **DIY** — You build it, forget expiry, badges stay forever. Users stop noticing.
35
+ ---
35
36
 
36
- ## The Solution
37
+ ## Why FeatureDrop?
37
38
 
38
- **featuredrop** is a free, self-hosted alternative to Beamer, Headway, and AnnounceKit. Zero deps, < 10 kB, headless-first.
39
+ Every SaaS ships features. Users miss them. The usual options are bad:
39
40
 
40
- ```
41
- npm install featuredrop # 0 dependencies, < 2 kB core
42
- ```
41
+ | Option | Problem |
42
+ |---|---|
43
+ | **Beamer / Headway / AnnounceKit** | External widget, vendor lock-in, $49–399/mo |
44
+ | **Pendo / Appcues** | Feature flags AND onboarding, ~$7k+/yr |
45
+ | **Joyride / Shepherd.js** | Tours only, not persistence or changelog |
46
+ | **DIY** | You build it, forget expiry, badges stay forever |
43
47
 
44
- ```
45
- ┌─────────────────────────────────────────┐
46
- │ ☰ My SaaS App 🔔 │
47
- ├──────────┬──────────────────────────────┤
48
- │ │ │
49
- │ Dashboard│ Welcome back, Sarah! │
50
- │ │ │
51
- │ Journal ●│ ┌─────────────────────┐ │
52
- │ │ │ What's New (2) │ │
53
- │ Analytics│ │ │ │
54
- │ NEW │ │ ★ AI Journal │ │
55
- │ │ │ Track decisions │ │
56
- │ Billing │ │ with AI insights. │ │
57
- │ │ │ [Try it →] │ │
58
- │ Settings │ │ │ │
59
- │ │ │ ★ Analytics v2 │ │
60
- │ │ │ Real-time charts │ │
61
- │ │ │ and CSV export. │ │
62
- │ │ │ │ │
63
- │ │ │ [Mark all as read] │ │
64
- │ │ └─────────────────────┘ │
65
- │ │ │
66
- └──────────┴──────────────────────────────┘
67
-
68
- ● = dot badge NEW = pill badge (2) = count badge
69
- ```
48
+ **FeatureDrop** gives you a free, self-hosted middle path: production-ready adoption components that run inside your own React tree, powered by a JSON manifest you own.
49
+
50
+ ---
70
51
 
71
52
  ## Quick Start
72
53
 
73
- **1. Define features** (just an array of objects):
54
+ ```bash
55
+ npm install featuredrop # < 3 kB core, zero runtime dependencies
56
+ ```
57
+
58
+ **1. Define your features:**
74
59
 
75
60
  ```ts
76
61
  import { createManifest } from 'featuredrop'
77
62
 
78
- export const FEATURES = createManifest([
63
+ export const features = createManifest([
79
64
  {
80
- id: 'ai-journal',
81
- label: 'AI Decision Journal',
82
- description: 'Track decisions with AI-powered insights.',
83
- releasedAt: '2026-02-20T00:00:00Z',
84
- showNewUntil: '2026-03-20T00:00:00Z',
85
- sidebarKey: '/journal',
65
+ id: 'dark-mode',
66
+ label: 'Dark Mode',
67
+ description: 'Full dark theme support across every surface.',
68
+ releasedAt: '2026-03-01T00:00:00Z',
69
+ showNewUntil: '2026-04-01T00:00:00Z',
86
70
  type: 'feature',
87
- priority: 'critical',
88
- cta: { label: 'Try it', url: '/journal' },
89
- },
90
- {
91
- id: 'scheduled-reports',
92
- label: 'Scheduled Reports',
93
- releasedAt: '2026-02-23T00:00:00Z',
94
- showNewUntil: '2026-03-23T00:00:00Z',
95
- dependsOn: { clicked: ['ai-journal'] }, // progressive rollout
96
- trigger: { type: 'page', match: '/reports/*' }, // contextual trigger
71
+ priority: 'high',
72
+ cta: { label: 'Try it', url: '/settings/appearance' },
97
73
  },
98
74
  ])
99
75
  ```
100
76
 
101
- **2. Create a storage adapter:**
77
+ **2. Wrap your app:**
102
78
 
103
- ```ts
79
+ ```tsx
80
+ import { FeatureDropProvider } from 'featuredrop/react'
104
81
  import { LocalStorageAdapter } from 'featuredrop'
82
+ import { features } from './features'
105
83
 
106
- const storage = new LocalStorageAdapter({
107
- watermark: user.featuresSeenAt, // from your server
108
- onDismissAll: (now) => api.markFeaturesSeen(now), // optional server sync
109
- })
110
- ```
111
-
112
- For offline-heavy apps, use `IndexedDBAdapter` to persist the same state with IndexedDB + local fallback.
113
- It also supports queued dismiss sync (`flushQueue`) + remote state reconciliation (`syncFromRemote`) for offline-first flows.
114
- For remote/state sync, `RemoteAdapter` now includes retry + circuit-breaker protection and a monitoring-friendly `isHealthy()` check.
115
-
116
- **3. Check what's new:**
117
-
118
- ```ts
119
- import { getNewFeatures, hasNewFeature } from 'featuredrop'
120
-
121
- const newFeatures = getNewFeatures(FEATURES, storage)
122
- hasNewFeature(FEATURES, '/journal', storage) // true/false
123
- ```
124
-
125
- Works with **any framework**. Zero React dependency for vanilla use.
126
-
127
- ## Adoption Analytics
128
-
129
- Pipe structured adoption events to PostHog/Amplitude/Mixpanel/Segment or your own endpoint.
130
-
131
- ```ts
132
- import { AnalyticsCollector, PostHogAdapter } from 'featuredrop'
133
-
134
- const collector = new AnalyticsCollector({
135
- adapter: new PostHogAdapter(posthog),
136
- batchSize: 20,
137
- flushInterval: 10_000,
138
- sampleRate: 1,
139
- })
140
- ```
141
-
142
- ```tsx
143
- <FeatureDropProvider
144
- manifest={FEATURES}
145
- storage={storage}
146
- collector={collector}
147
- >
84
+ <FeatureDropProvider manifest={features} storage={new LocalStorageAdapter()}>
148
85
  <App />
149
86
  </FeatureDropProvider>
150
87
  ```
151
88
 
152
- ## A/B Announcement Variants
153
-
154
- Run deterministic per-user announcement variants with weighted splits.
155
-
156
- ```ts
157
- {
158
- id: 'ai-journal',
159
- label: 'AI Decision Journal',
160
- variants: {
161
- control: { description: 'Track decisions with AI-powered insights.' },
162
- treatment: { description: 'Never second-guess decisions again.' },
163
- },
164
- variantSplit: [50, 50],
165
- }
166
- ```
89
+ **3. Add badges and a changelog:**
167
90
 
168
91
  ```tsx
169
- <FeatureDropProvider
170
- manifest={FEATURES}
171
- storage={storage}
172
- variantKey={user.id} // stable key for deterministic assignment
173
- >
174
- <App />
175
- </FeatureDropProvider>
176
- ```
92
+ import { NewBadge, ChangelogWidget } from 'featuredrop/react'
177
93
 
178
- ## Theme Engine
94
+ // Sidebar nav item
95
+ <a href="/settings">
96
+ Settings <NewBadge id="dark-mode" /> {/* auto-expires */}
97
+ </a>
179
98
 
180
- Theme all featuredrop components with CSS variables (no CSS-in-JS runtime).
181
-
182
- ```tsx
183
- import { ThemeProvider, ChangelogWidget } from 'featuredrop/react'
184
-
185
- <ThemeProvider theme="dark">
186
- <ChangelogWidget />
187
- </ThemeProvider>
99
+ // Changelog button
100
+ <ChangelogWidget title="What's new" showReactions />
188
101
  ```
189
102
 
190
- ```tsx
191
- import { createTheme } from 'featuredrop'
192
- import { ThemeProvider, ChangelogPage } from 'featuredrop/react'
103
+ That's it. Badges expire on schedule. No database setup. No vendor account. No tracking pixels.
193
104
 
194
- const myTheme = createTheme({
195
- colors: { primary: '#7c3aed' },
196
- radii: { lg: '16px' },
197
- })
105
+ **Full walkthrough:** [10-minute quickstart](https://featuredrop.dev/docs/quickstart)
198
106
 
199
- <ThemeProvider theme={myTheme}>
200
- <ChangelogPage />
201
- </ThemeProvider>
202
- ```
107
+ ---
203
108
 
204
- Presets: `light`, `dark`, `auto`, `minimal`, `vibrant`.
205
- You can also pass `theme` directly to `ChangelogWidget` and `ChangelogPage` for component-scoped overrides.
109
+ ## Components
206
110
 
207
- ## Internationalization
111
+ Everything you'd get from Beamer or Pendo, but free, self-hosted, and headless-first.
112
+
113
+ | Component | Description |
114
+ |---|---|
115
+ | `<ChangelogWidget>` | Trigger button + slide-out/modal changelog with emoji reactions |
116
+ | `<ChangelogPage>` | Full-page changelog with filters, search, and pagination |
117
+ | `<NewBadge>` | Auto-expiring pill / dot / count badge |
118
+ | `<Banner>` | Top-of-page or inline banner with `announcement`, `warning`, `info` variants |
119
+ | `<Toast>` | Stackable toast notifications with auto-dismiss and position control |
120
+ | `<Tour>` | Multi-step guided product tours with keyboard nav and persistence |
121
+ | `<Checklist>` | Onboarding task checklists with progress tracking |
122
+ | `<Spotlight>` | Pulsing DOM-attached beacon/tooltip |
123
+ | `<SpotlightChain>` | Chained spotlight walkthrough ("here are 3 new things") |
124
+ | `<AnnouncementModal>` | Priority-gated modal with optional image carousel |
125
+ | `<Hotspot>` / `<TooltipGroup>` | Persistent contextual hints with visibility caps |
126
+ | `<FeedbackWidget>` | In-app feedback with category, emoji, screenshot support |
127
+ | `<Survey>` | NPS / CSAT / CES / custom survey engine with trigger rules |
128
+ | `<FeatureRequestButton>` | Per-feature voting button with vote guard |
129
+ | `<FeatureRequestForm>` | Request capture + sortable request list |
130
+
131
+ All components are headless-capable via render props. [See live demos →](https://featuredrop.dev/docs/components/gallery)
208
132
 
209
- Use built-in locale packs or supply partial overrides:
133
+ ---
210
134
 
211
- ```tsx
212
- <FeatureDropProvider
213
- manifest={FEATURES}
214
- storage={storage}
215
- locale="fr"
216
- translations={{
217
- submit: 'Envoyer maintenant',
218
- }}
219
- >
220
- <App />
221
- </FeatureDropProvider>
222
- ```
135
+ ## How It Works
223
136
 
224
- Built-in locales: `en`, `es`, `fr`, `de`, `pt`, `zh-cn`, `ja`, `ko`, `ar`, `hi`.
225
- Locale helpers include locale-aware date formatting, pluralized count copy, and RTL direction support for Arabic.
226
- Use `formatRelativeTimeForLocale()` for localized "x days ago" labels, or set `dateFormat="relative"` on `ChangelogWidget` / `ChangelogPage`.
137
+ ```
138
+ Manifest (static) Storage (runtime)
139
+ ┌─────────────────────┐ ┌──────────────────────┐
140
+ │ releasedAt: Mar 1 │ │ watermark ← server │
141
+ │ showNewUntil: Apr 1 │ │ dismissed ← localStorage │
142
+ └──────────┬──────────┘ └──────────┬───────────┘
143
+ │ │
144
+ └──────────┐ ┌─────────────────┘
145
+ ▼ ▼
146
+ ┌───────────────┐
147
+ │ isNew() │
148
+ │ │
149
+ │ !dismissed │
150
+ │ !expired │
151
+ │ afterWatermark│
152
+ └───────┬───────┘
153
+
154
+ true / false
155
+ ```
227
156
 
228
- ## Animation Presets
157
+ New users see everything (no watermark). Returning users see only features shipped since their last visit. Dismissals are instant (localStorage). "Mark all seen" syncs across devices with one optional server write.
229
158
 
230
- Choose built-in motion levels globally:
159
+ Read the full [Architecture doc](https://featuredrop.dev/docs/concepts/architecture) for cross-device sync and custom adapter patterns.
231
160
 
232
- ```tsx
233
- <FeatureDropProvider manifest={FEATURES} storage={storage} animation="subtle">
234
- <App />
235
- </FeatureDropProvider>
236
- ```
161
+ ---
237
162
 
238
- Available presets: `none`, `subtle`, `normal`, `playful`.
239
- Reduced-motion users automatically receive `none`.
240
- Built-in `ChangelogWidget`, `AnnouncementModal`, and `Toast` include enter/exit animations, and `Tour`, `Survey`, `FeedbackWidget`, `Spotlight`, `SpotlightChain`, and `Hotspot` follow the same preset for enter/pulse motion.
163
+ ## Storage Adapters
241
164
 
242
- ## Custom Renderer Protocol
165
+ | Adapter | Import | Best For |
166
+ |---|---|---|
167
+ | `LocalStorageAdapter` | `featuredrop` | Browser apps (default) |
168
+ | `MemoryAdapter` | `featuredrop` | Testing, SSR |
169
+ | `IndexedDBAdapter` | `featuredrop/adapters` | Offline-first PWAs |
170
+ | `RemoteAdapter` | `featuredrop/adapters` | Server-backed with retry + circuit-breaker |
171
+ | `HybridAdapter` | `featuredrop/adapters` | Local + remote with batched flush |
172
+ | Redis / PostgreSQL / DynamoDB | `featuredrop/adapters` | Database-backed server-side apps |
243
173
 
244
- Need full UI control? Use a UI-agnostic renderer with state/actions/computed helpers:
174
+ [All adapters →](https://featuredrop.dev/docs/adapters/overview)
245
175
 
246
- ```ts
247
- import { createChangelogRenderer } from 'featuredrop'
176
+ ---
248
177
 
249
- const renderer = createChangelogRenderer({ manifest: FEATURES, storage })
250
- renderer.subscribe((state) => console.log(state.newCount))
251
- renderer.actions.dismiss('ai-journal')
252
- ```
178
+ ## CLI
253
179
 
254
- ## Accessibility
180
+ Manage your manifest from the command line:
255
181
 
256
- featuredrop components include keyboard navigation, focus trapping/return for dialogs, ARIA live regions, and reduced-motion support.
182
+ ```bash
183
+ # Scaffold
184
+ npx featuredrop init
185
+ npx featuredrop add --label "Dark Mode" --category ui --type feature
257
186
 
258
- Automated accessibility regression checks run in CI via `axe-core` + `vitest-axe` (`src/__tests__/accessibility-axe.test.tsx`).
187
+ # Validate & audit
188
+ npx featuredrop validate # schema + duplicate ID check
189
+ npx featuredrop doctor # security + best practice audit
190
+ npx featuredrop stats # manifest summary stats
259
191
 
260
- ## Changelog-as-Code
192
+ # Build (markdown → JSON)
193
+ npx featuredrop build --pattern "features/**/*.md" --out featuredrop.manifest.json
261
194
 
262
- Manage announcements as markdown files in your repo and compile to a manifest:
195
+ # Generate outputs
196
+ npx featuredrop generate-rss --out featuredrop.rss.xml
197
+ npx featuredrop generate-changelog --out CHANGELOG.generated.md
263
198
 
264
- ```bash
265
- npx featuredrop init --format markdown
266
- npx featuredrop add --label "AI Journal" --category ai --description "Track decisions with AI."
267
- npx featuredrop build --pattern "features/**/*.md" --out featuredrop.manifest.json
268
- npx featuredrop validate --pattern "features/**/*.md"
269
- npx featuredrop stats --pattern "features/**/*.md"
270
- npx featuredrop doctor --pattern "features/**/*.md"
271
- npx featuredrop generate-rss --pattern "features/**/*.md" --out featuredrop.rss.xml
272
- npx featuredrop generate-changelog --pattern "features/**/*.md" --out CHANGELOG.generated.md
273
- npx featuredrop migrate --from beamer --input beamer-export.json --out featuredrop.manifest.json
274
- npx featuredrop migrate --from headway --input headway-export.json --out featuredrop.manifest.json
275
- npx featuredrop migrate --from announcekit --input announcekit-export.json --out featuredrop.manifest.json
276
- npx featuredrop migrate --from canny --input canny-export.json --out featuredrop.manifest.json
277
- npx featuredrop migrate --from launchnotes --input launchnotes-export.json --out featuredrop.manifest.json
199
+ # Migrate from vendors
200
+ npx featuredrop migrate --from beamer --input beamer-export.json
201
+ npx featuredrop migrate --from headway --input headway-export.json
202
+ npx featuredrop migrate --from announcekit --input announcekit-export.json
203
+ npx featuredrop migrate --from canny --input canny-export.json
204
+ npx featuredrop migrate --from launchnotes --input launchnotes-export.json
278
205
  ```
279
206
 
280
- Example feature file:
207
+ [CLI reference →](https://featuredrop.dev/docs/automation/ci)
281
208
 
282
- ```md
283
- ---
284
- id: ai-journal
285
- label: AI Journal
286
- type: feature
287
- category: ai
288
- releasedAt: 2026-02-15T00:00:00Z
289
- showNewUntil: 2026-03-15T00:00:00Z
290
- cta:
291
- label: Try it now
292
- url: /journal
293
209
  ---
294
- Track decisions and outcomes with AI-powered insights.
295
- ```
296
210
 
297
- ## CMS Adapters
211
+ ## Framework Adapters
298
212
 
299
- Pull releases from your CMS and map them into `FeatureEntry[]`:
213
+ | Framework | Status | Import |
214
+ |---|---|---|
215
+ | React / Next.js | ✅ Stable | `featuredrop/react` |
216
+ | Vanilla JS | ✅ Stable | `featuredrop` |
217
+ | SolidJS | 🔬 Preview | `featuredrop/solid` |
218
+ | Preact | 🔬 Preview | `featuredrop/preact` |
219
+ | Web Components | 🔬 Preview | `featuredrop/web-components` |
220
+ | Angular | 🔬 Preview | `featuredrop/angular` |
221
+ | Vue 3 | 🔬 Preview | `featuredrop/vue` |
222
+ | Svelte 5 | 🔬 Preview | `featuredrop/svelte` |
300
223
 
301
- ```ts
302
- import { ContentfulAdapter, SanityAdapter, StrapiAdapter, NotionAdapter, MarkdownAdapter } from 'featuredrop'
224
+ ---
303
225
 
304
- const contentful = new ContentfulAdapter({
305
- spaceId: process.env.CONTENTFUL_SPACE!,
306
- accessToken: process.env.CONTENTFUL_TOKEN!,
307
- contentType: 'featureRelease',
308
- })
226
+ ## React Hooks
309
227
 
310
- const entries = await contentful.load()
311
- ```
228
+ | Hook | Returns |
229
+ |---|---|
230
+ | `useFeatureDrop()` | Full context: features, count, dismiss, throttle controls |
231
+ | `useNewFeature(key)` | Single nav item: `{ isNew, feature, dismiss }` |
232
+ | `useNewCount()` | Current unread badge count |
233
+ | `useTour(id)` | Imperative tour controls and step snapshot |
234
+ | `useTourSequencer(sequence)` | Ordered multi-tour orchestration |
235
+ | `useChecklist(id)` | Checklist progress + task controls |
236
+ | `useSurvey(id)` | Survey controls: `show`, `hide`, `askLater` |
237
+ | `useTabNotification()` | Browser tab title count: `"(3) My App"` |
312
238
 
313
- Each adapter accepts optional `fieldMapping` overrides so you can map your content model fields without reshaping server responses.
239
+ ---
314
240
 
315
241
  ## Notification Bridges
316
242
 
317
- Use framework-agnostic bridge helpers to fan out release notifications:
243
+ Fan out release notifications to Slack, Discord, email, webhooks, or RSS on deploy:
318
244
 
319
245
  ```ts
320
- import {
321
- SlackBridge,
322
- DiscordBridge,
323
- EmailDigestGenerator,
324
- WebhookBridge,
325
- RSSFeedGenerator,
326
- } from 'featuredrop/bridges'
246
+ import { SlackBridge, DiscordBridge, WebhookBridge, EmailDigestGenerator, RSSFeedGenerator } from 'featuredrop/bridges'
327
247
 
328
248
  await SlackBridge.notify(feature, { webhookUrl: process.env.SLACK_WEBHOOK! })
329
249
  await DiscordBridge.notify(feature, { webhookUrl: process.env.DISCORD_WEBHOOK! })
330
250
  await WebhookBridge.post(feature, { url: 'https://api.example.com/hooks/features' })
331
251
 
332
- const html = EmailDigestGenerator.generate(features, { title: 'Weekly updates' })
333
- const rss = RSSFeedGenerator.generate(features, { title: 'Product Updates' })
252
+ const html = EmailDigestGenerator.generate(features, { title: 'Weekly Product Updates' })
253
+ const rss = RSSFeedGenerator.generate(features, { title: 'Product Updates' })
334
254
  ```
335
255
 
336
- ## CI Utilities
337
-
338
- Use `featuredrop/ci` for manifest diffing in pull-request checks:
339
-
340
- ```ts
341
- import { diffManifest, generateChangelogDiff, validateManifestForCI } from 'featuredrop/ci'
256
+ ---
342
257
 
343
- const diff = diffManifest(beforeManifest, afterManifest)
344
- const summary = generateChangelogDiff(diff, { includeFieldChanges: true })
345
- const validation = validateManifestForCI(afterManifest)
346
- ```
258
+ ## Analytics
347
259
 
348
- Run bundle budget checks after `pnpm build`:
260
+ Pipe adoption events to any analytics provider:
349
261
 
350
- ```bash
351
- pnpm size-check
262
+ ```tsx
263
+ <FeatureDropProvider
264
+ manifest={features}
265
+ storage={storage}
266
+ analytics={{
267
+ onFeatureSeen: (f) => posthog.capture('feature_seen', { id: f.id }),
268
+ onFeatureDismissed: (f) => posthog.capture('feature_dismissed', { id: f.id }),
269
+ onFeatureClicked: (f) => posthog.capture('feature_clicked', { id: f.id }),
270
+ onWidgetOpened: () => posthog.capture('changelog_opened'),
271
+ }}
272
+ >
273
+ <App />
274
+ </FeatureDropProvider>
352
275
  ```
353
276
 
354
- ## Feature Flag Bridges
355
-
356
- Gate announcement visibility behind flags via `feature.flagKey`:
357
-
358
- ```ts
359
- import { createFlagBridge, LaunchDarklyBridge, PostHogBridge } from 'featuredrop/flags'
277
+ Works with PostHog, Mixpanel, Amplitude, Segment, or any custom endpoint.
360
278
 
361
- const bridge = createFlagBridge({
362
- isEnabled: (flagKey) => myFlagService.isOn(flagKey),
363
- })
364
-
365
- // Provider + feature entry
366
- <FeatureDropProvider manifest={FEATURES} storage={storage} flagBridge={bridge} />
367
- // { id: 'ai-journal', flagKey: 'ai-journal-enabled', ... }
368
-
369
- // Or vendor bridges
370
- const ld = new LaunchDarklyBridge(ldClient)
371
- const ph = new PostHogBridge(posthog)
372
- ```
279
+ ---
373
280
 
374
- ## Multi-Product Support
281
+ ## User Segmentation
375
282
 
376
- Scope visibility by product using `FeatureDropProvider` + `feature.product`:
283
+ Show the right features to the right users:
377
284
 
378
285
  ```tsx
379
- <FeatureDropProvider manifest={FEATURES} storage={storage} product="askverdict" />
380
- // feature.product can be "askverdict", "other-product", or "*" for shared entries
286
+ <FeatureDropProvider
287
+ manifest={features}
288
+ storage={storage}
289
+ userContext={{ plan: 'pro', role: 'admin', region: 'eu' }}
290
+ >
291
+ <App />
292
+ </FeatureDropProvider>
381
293
  ```
382
294
 
383
- ## Admin Components
295
+ Define audience rules per feature in your manifest:
384
296
 
385
- Use optional admin UI primitives via `featuredrop/admin`:
386
-
387
- ```tsx
388
- import {
389
- ManifestEditor,
390
- ScheduleCalendar,
391
- PreviewPanel,
392
- AudienceBuilder,
393
- } from 'featuredrop/admin'
394
-
395
- <ManifestEditor features={features} onSave={saveManifest} />
396
- <ScheduleCalendar features={features} onSchedule={scheduleFeature} />
397
- <PreviewPanel feature={selectedFeature} components={['badge', 'changelog', 'toast']} />
398
- <AudienceBuilder segments={['free', 'pro']} roles={['admin', 'viewer']} onSave={saveAudience} />
297
+ ```json
298
+ {
299
+ "id": "ai-copilot",
300
+ "label": "AI Copilot",
301
+ "audience": { "plan": ["pro", "enterprise"], "region": ["us", "eu"] }
302
+ }
399
303
  ```
400
304
 
401
- ## Resilience
305
+ Users outside the audience never see the feature. No server calls. No feature flag service needed.
402
306
 
403
- Built-in React exports are wrapped in internal error boundaries. If a featuredrop component throws, it unmounts itself (no host app crash) and optionally reports through provider `onError`.
307
+ ---
404
308
 
405
- ## Schema Validation
309
+ ## CI Integration
406
310
 
407
- Validate manifest JSON in CI or tooling pipelines.
311
+ Validate your manifest in every pull request:
408
312
 
409
313
  ```ts
410
314
  import {
411
- featureEntrySchema,
412
- featureEntryJsonSchema,
413
- validateManifest,
414
- } from 'featuredrop/schema'
415
-
416
- featureEntrySchema.parse({
417
- id: 'ai-journal',
418
- label: 'AI Journal',
419
- releasedAt: '2026-02-15T00:00:00Z',
420
- showNewUntil: '2026-03-15T00:00:00Z',
421
- })
422
-
423
- const result = validateManifest(data)
424
- if (!result.valid) {
425
- throw new Error(result.errors.map((e) => `${e.path}: ${e.message}`).join("; "))
426
- }
427
-
428
- console.log(featureEntryJsonSchema.properties.id.type) // "string"
429
- ```
430
-
431
- ## Testing Utilities
432
-
433
- Use `featuredrop/testing` to speed up unit and component tests.
434
-
435
- ```tsx
436
- import { render, screen } from '@testing-library/react'
437
- import { useNewCount } from 'featuredrop/react'
438
- import { createMockManifest, createMockStorage, createTestProvider } from 'featuredrop/testing'
439
-
440
- const manifest = createMockManifest([{ label: 'AI Journal', releasedAt: 'today', showNewUntil: '+14d' }])
441
- const storage = createMockStorage()
442
- const Wrapper = createTestProvider({ manifest, storage })
443
-
444
- function Count() {
445
- return <span>{useNewCount()}</span>
446
- }
447
-
448
- render(<Count />, { wrapper: Wrapper })
449
- expect(screen.getByText('1')).toBeInTheDocument()
450
- ```
451
-
452
- ## Playground & Online Demos
453
-
454
- Use the lightweight component playground for quick UI iteration:
455
-
456
- ```bash
457
- pnpm --dir examples/sandbox-react install
458
- pnpm playground
459
- pnpm playground:build
315
+ diffManifest,
316
+ generateChangelogDiff,
317
+ generateChangelogDiffMarkdown,
318
+ validateManifestForCI
319
+ } from 'featuredrop/ci'
320
+
321
+ const diff = diffManifest(beforeManifest, afterManifest)
322
+ const summary = generateChangelogDiff(diff, { includeFieldChanges: true })
323
+ const markdown = generateChangelogDiffMarkdown(diff, { includeFieldChanges: true })
324
+ const validation = validateManifestForCI(afterManifest)
460
325
  ```
461
326
 
462
- One-click editable demos:
463
-
464
- - React sandbox source: `examples/sandbox-react`
465
- - StackBlitz: https://stackblitz.com/github/GLINCKER/featuredrop/tree/main/examples/sandbox-react
466
- - CodeSandbox: https://codesandbox.io/p/sandbox/github/GLINCKER/featuredrop/tree/main/examples/sandbox-react
467
-
468
- ## Docs Site (Nextra + Vercel)
469
-
470
- The repo now includes a docs app scaffold at `apps/docs` for a fast validation launch on Vercel.
471
-
472
327
  ```bash
473
- pnpm docs:install
474
- pnpm docs:dev
475
- pnpm docs:build
328
+ pnpm size-check # bundle budget check post-build
476
329
  ```
477
330
 
478
- The docs app is static-export ready, so it can also deploy on Cloudflare Pages or GitHub Pages.
331
+ [CI setup guide →](https://featuredrop.dev/docs/automation/ci)
479
332
 
480
- ## Components
481
-
482
- Everything you'd expect from Beamer or Headway — but free, self-hosted, and headless-first.
483
-
484
- ### Changelog Widget
485
-
486
- The #1 feature people install these tools for. Trigger button with unread count badge, slide-out panel with rich changelog feed.
487
-
488
- ```tsx
489
- import { ChangelogWidget } from 'featuredrop/react'
490
-
491
- // Default: slide-out panel with all features
492
- <ChangelogWidget />
493
-
494
- // Or modal / popover variant
495
- <ChangelogWidget variant="modal" title="Release Notes" />
496
-
497
- // Enable emoji reactions on entries
498
- <ChangelogWidget showReactions />
499
-
500
- // Fully headless
501
- <ChangelogWidget>
502
- {({ isOpen, toggle, features, count, dismissAll }) => (
503
- <YourCustomUI />
504
- )}
505
- </ChangelogWidget>
506
- ```
507
-
508
- ### Spotlight Beacon
509
-
510
- Pulsing beacon that attaches to any DOM element. Click to see feature tooltip.
511
-
512
- ```tsx
513
- import { Spotlight } from 'featuredrop/react'
514
-
515
- const ref = useRef<HTMLButtonElement>(null)
516
- <button ref={ref}>Analytics</button>
517
- <Spotlight featureId="analytics-v2" targetRef={ref} />
518
-
519
- // Or with CSS selector
520
- <Spotlight featureId="analytics-v2" targetSelector="#analytics-btn" />
521
- ```
522
-
523
- ### Announcement Banner
524
-
525
- Top-of-page or inline banner for major announcements. Auto-expires like badges.
526
-
527
- ```tsx
528
- import { Banner } from 'featuredrop/react'
529
-
530
- <Banner featureId="v2-launch" variant="announcement" />
531
- <Banner featureId="breaking-change" variant="warning" />
532
- <Banner featureId="security-fix" variant="info" position="fixed" />
533
- ```
534
-
535
- ### Toast Notifications
536
-
537
- Brief popup notifications for new features. Auto-dismiss, stackable, configurable position.
538
-
539
- ```tsx
540
- import { Toast } from 'featuredrop/react'
541
-
542
- <Toast position="bottom-right" maxVisible={3} />
543
-
544
- // Specific features only
545
- <Toast featureIds={["ai-journal"]} autoDismissMs={5000} />
546
- ```
547
-
548
- ### Product Tours
549
-
550
- Guided, multi-step onboarding with keyboard navigation and persistence.
551
-
552
- ```tsx
553
- import { Tour, useTour } from 'featuredrop/react'
554
-
555
- <Tour id="onboarding" steps={steps} />
556
- const { startTour, nextStep, skipTour } = useTour('onboarding')
557
- ```
558
-
559
- ### Onboarding Checklist
560
-
561
- Task-based onboarding that can trigger tours, links, or callbacks.
562
-
563
- ```tsx
564
- import { Checklist, useChecklist } from 'featuredrop/react'
565
-
566
- <Checklist id="getting-started" tasks={tasks} />
567
- const { completeTask, progress } = useChecklist('getting-started')
568
- ```
569
-
570
- ### Feedback Widget
571
-
572
- Collect lightweight in-app feedback with optional emoji and screenshots.
573
-
574
- ```tsx
575
- import { FeedbackWidget } from 'featuredrop/react'
576
-
577
- <FeedbackWidget
578
- featureId="ai-journal"
579
- onSubmit={async (payload) => {
580
- await fetch('/api/feedback', { method: 'POST', body: JSON.stringify(payload) })
581
- }}
582
- showEmoji
583
- showScreenshot
584
- rateLimit="1-per-feature"
585
- />
586
- ```
587
-
588
- ### Survey (NPS / CSAT / CES / Custom)
589
-
590
- Run micro-surveys with trigger rules and imperative controls.
591
-
592
- ```tsx
593
- import { Survey, useSurvey } from 'featuredrop/react'
594
-
595
- <Survey
596
- id="nps-main"
597
- type="nps"
598
- prompt="How likely are you to recommend us?"
599
- triggerRules={{ minDaysSinceSignup: 7, signupAt: user.createdAt }}
600
- onSubmit={saveSurvey}
601
- />
602
-
603
- const { show } = useSurvey('nps-main')
604
- ```
605
-
606
- ### Feature Request Voting
607
-
608
- Capture and rank product requests with local persistence and optional webhook sync.
609
-
610
- ```tsx
611
- import { FeatureRequestButton, FeatureRequestForm } from 'featuredrop/react'
612
-
613
- <FeatureRequestButton featureId="dark-mode" requestTitle="Dark mode" />
614
-
615
- <FeatureRequestForm
616
- categories={['UI', 'Performance', 'Integration', 'Other']}
617
- onSubmit={async (request) => {
618
- await fetch('/api/requests', { method: 'POST', body: JSON.stringify(request) })
619
- }}
620
- />
621
- ```
622
-
623
- ### Hotspots & Tooltips
624
-
625
- Persistent contextual hints attached to specific UI targets.
626
-
627
- ```tsx
628
- import { Hotspot, TooltipGroup } from 'featuredrop/react'
629
-
630
- <TooltipGroup maxVisible={1}>
631
- <Hotspot id="export-help" target="#export-btn" frequency="once">
632
- Export supports CSV, PDF, and Excel.
633
- </Hotspot>
634
- </TooltipGroup>
635
- ```
636
-
637
- ### Announcement Modal
638
-
639
- Priority-based modal announcements with optional slide carousel.
640
-
641
- ```tsx
642
- import { AnnouncementModal } from 'featuredrop/react'
643
-
644
- <AnnouncementModal
645
- feature={criticalFeature}
646
- trigger="auto"
647
- frequency="once"
648
- />
649
- ```
650
-
651
- ### Spotlight Chain
652
-
653
- Lightweight chained spotlights for "here are 3 new things" flows.
654
-
655
- ```tsx
656
- import { SpotlightChain } from 'featuredrop/react'
657
-
658
- <SpotlightChain
659
- steps={[
660
- { target: '#sidebar', content: 'New navigation' },
661
- { target: '#search', content: 'Global search' },
662
- ]}
663
- />
664
- ```
665
-
666
- ### NewBadge
667
-
668
- Headless badge component with variants. Zero CSS framework dependency.
669
-
670
- ```tsx
671
- import { NewBadge } from 'featuredrop/react'
672
-
673
- <NewBadge /> // "New" pill
674
- <NewBadge variant="dot" /> // Pulsing dot
675
- <NewBadge variant="count" count={3} /> // Count badge
676
- ```
677
-
678
- ### Tab Title Notification
679
-
680
- Updates the browser tab title with unread count. Restores when all read.
681
-
682
- ```tsx
683
- import { useTabNotification } from 'featuredrop/react'
684
-
685
- useTabNotification() // "(3) My App"
686
- useTabNotification({ template: "[{count} new] {title}", flash: true })
687
- ```
333
+ ---
688
334
 
689
- ## React
335
+ ## Migration from Beamer / Pendo
690
336
 
691
337
  ```bash
692
- npm install featuredrop react # react is an optional peer dep
338
+ npx featuredrop migrate --from beamer --input beamer-export.json --out features.json
693
339
  ```
694
340
 
695
- **Wrap your app:**
696
-
697
- ```tsx
698
- import { FeatureDropProvider } from 'featuredrop/react'
699
-
700
- <FeatureDropProvider manifest={FEATURES} storage={storage} appVersion="2.5.1">
701
- <App />
702
- </FeatureDropProvider>
703
- ```
341
+ | | Beamer | Pendo | **FeatureDrop** |
342
+ |---|---|---|---|
343
+ | Price | $59–399/mo | $7k+/yr | **Free forever** |
344
+ | Bundle impact | External script | ~300 kB agent | **< 3 kB core** |
345
+ | Vendor lock-in | Yes | Yes | **No** |
346
+ | Data ownership | Vendor-hosted | Vendor-hosted | **Your repo** |
347
+ | Customization | CSS themes | Limited | **Full source access** |
704
348
 
705
- **Throttling + quiet mode:**
349
+ [Full migration guide →](https://featuredrop.dev/docs/migration)
706
350
 
707
- ```tsx
708
- <FeatureDropProvider
709
- manifest={FEATURES}
710
- storage={storage}
711
- throttle={{
712
- maxSimultaneousBadges: 3,
713
- maxSimultaneousSpotlights: 1,
714
- maxToastsPerSession: 3,
715
- minTimeBetweenModals: 30000,
716
- minTimeBetweenTours: 86400000,
717
- sessionCooldown: 5000,
718
- respectDoNotDisturb: true,
719
- }}
720
- >
721
- <App />
722
- </FeatureDropProvider>
723
- ```
724
-
725
- **Add badges to your sidebar:**
726
-
727
- ```tsx
728
- import { useNewFeature, NewBadge } from 'featuredrop/react'
729
-
730
- function SidebarItem({ path, label }: { path: string; label: string }) {
731
- const { isNew, dismiss } = useNewFeature(path)
732
- return (
733
- <a href={path} onClick={() => isNew && dismiss()}>
734
- {label}
735
- {isNew && <NewBadge />}
736
- </a>
737
- )
738
- }
739
- ```
740
-
741
- **Or drop in the full changelog widget:**
742
-
743
- ```tsx
744
- import { ChangelogWidget } from 'featuredrop/react'
745
-
746
- <ChangelogWidget variant="panel" />
747
- ```
748
-
749
- **Hooks & Components:**
750
-
751
- | Export | What it does |
752
- |--------|-------------|
753
- | `useFeatureDrop()` | Full context: features, count, dismiss, dismissAll |
754
- | `useNewFeature(key)` | Single nav item: `{ isNew, feature, dismiss }` |
755
- | `useNewCount()` | Just the badge count |
756
- | `useTabNotification()` | Updates browser tab title with count |
757
- | `useTour(id)` | Imperative tour controls and current step snapshot |
758
- | `useTourSequencer(sequence)` | Ordered multi-tour orchestration by feature readiness |
759
- | `useChecklist(id)` | Imperative checklist controls + progress |
760
- | `useSurvey(id)` | Imperative survey controls (`show`, `hide`, `askLater`) + state |
761
- | `<NewBadge />` | Headless badge: `pill`, `dot`, or `count` variant |
762
- | `<ChangelogWidget />` | Full changelog feed with trigger button + optional reactions |
763
- | `<ChangelogPage />` | Full-page changelog with filters/search/pagination |
764
- | `<Spotlight />` | Pulsing beacon attached to DOM elements |
765
- | `<Banner />` | Announcement banner with variants |
766
- | `<Toast />` | Stackable toast notifications |
767
- | `<Tour />` | Multi-step guided product tour |
768
- | `<Checklist />` | Onboarding task checklist |
769
- | `<FeedbackWidget />` | In-app feedback form with category/emoji/screenshot support |
770
- | `<Survey />` | NPS/CSAT/CES/custom survey engine with trigger rules |
771
- | `<FeatureRequestButton />` | Per-feature voting button with persisted vote guard |
772
- | `<FeatureRequestForm />` | Request capture form + sortable request list |
773
- | `<Hotspot />` / `<TooltipGroup />` | Contextual tooltips with visibility caps |
774
- | `<AnnouncementModal />` | Priority/frequency-gated modal announcements |
775
- | `<SpotlightChain />` | Lightweight chained spotlight walkthrough |
776
-
777
- `useFeatureDrop()` also exposes queue/throttle controls: `queuedFeatures`, `totalNewCount`, `quietMode`, `setQuietMode`, `markFeatureSeen`, `markFeatureClicked`, toast-slot helpers, modal/tour pacing checks, and spotlight slot controls.
778
- It also exposes trigger runtime helpers: `trackUsageEvent`, `trackTriggerEvent`, `trackMilestone`, and `setTriggerPath`.
779
-
780
- **Analytics integration:**
781
-
782
- ```tsx
783
- <FeatureDropProvider
784
- manifest={FEATURES}
785
- storage={storage}
786
- appVersion="2.5.1" // optional semver for version-pinned features
787
- analytics={{
788
- onFeatureSeen: (f) => posthog.capture('feature_seen', { id: f.id }),
789
- onFeatureDismissed: (f) => posthog.capture('feature_dismissed', { id: f.id }),
790
- onFeatureClicked: (f) => posthog.capture('feature_clicked', { id: f.id }),
791
- onWidgetOpened: () => posthog.capture('changelog_opened'),
792
- }}
793
- >
794
- ```
795
-
796
- All components accept an optional `analytics` prop for component-level tracking too.
797
-
798
- ## How It Works
351
+ ---
799
352
 
800
- ```
801
- Manifest (static) Storage (runtime)
802
- ┌───────────────────┐ ┌──────────────────────┐
803
- │ releasedAt: Feb 20 │ │ watermark ← server │
804
- │ showNewUntil: Mar 20│ │ dismissed ← localStorage│
805
- └────────┬──────────┘ └──────────┬───────────┘
806
- │ │
807
- └──────────┐ ┌────────────────┘
808
- ▼ ▼
809
- ┌───────────────┐
810
- │ isNew() │
811
- │ │
812
- │ !dismissed │
813
- │ !expired │
814
- │ afterWatermark│
815
- │ afterPublishAt│
816
- └───────┬───────┘
817
-
818
- true / false
819
- ```
353
+ ## Full Comparison
820
354
 
821
- New users see everything (no watermark). Returning users only see features shipped since their last visit. Individual dismissals are instant (localStorage). "Mark all seen" syncs across devices (one server write).
355
+ | | FeatureDrop | Beamer | Headway | AnnounceKit | Pendo |
356
+ |---|:---:|:---:|:---:|:---:|:---:|
357
+ | **Price** | **Free** | $59–399/mo | $49–249/mo | $79–299/mo | $7k+/yr |
358
+ | Auto-expiring badges | ✅ | — | — | — | — |
359
+ | Changelog widget | ✅ | ✅ | ✅ | ✅ | ✅ |
360
+ | Product tours | ✅ | — | — | — | ✅ |
361
+ | Onboarding checklists | ✅ | — | — | — | ✅ |
362
+ | Spotlight / beacon | ✅ | — | — | — | — |
363
+ | Hotspot tooltips | ✅ | — | — | — | — |
364
+ | Announcement modal | ✅ | — | — | — | — |
365
+ | Toast notifications | ✅ | — | — | — | — |
366
+ | Feedback & surveys | ✅ | — | — | — | ✅ |
367
+ | Feature request voting | ✅ | — | — | — | — |
368
+ | Tab title notification | ✅ | — | — | — | — |
369
+ | Zero runtime deps (core) | ✅ | — | — | — | — |
370
+ | Framework agnostic | ✅ | — | — | — | — |
371
+ | Headless mode | ✅ | — | — | — | — |
372
+ | Analytics callbacks | ✅ | ✅ | ✅ | ✅ | ✅ |
373
+ | Self-hosted | ✅ | — | — | — | — |
374
+ | Open source | ✅ | — | — | — | — |
822
375
 
823
- **Scheduled publishing**: Set `publishAt` to hide entries until a specific date — ship code now, reveal later.
376
+ ---
824
377
 
825
- **Priority sorting**: Critical features surface first in widgets and toasts. Priority levels: `critical`, `normal`, `low`.
378
+ ## Documentation
826
379
 
827
- **Entry types**: `feature`, `improvement`, `fix`, `breaking` — each with default icon/color in built-in components.
380
+ | Resource | Description |
381
+ |---|---|
382
+ | [**Live Docs**](https://featuredrop.dev) | Full documentation site |
383
+ | [Quickstart](https://featuredrop.dev/docs/quickstart) | Ship your first badge in 10 minutes |
384
+ | [Component Gallery](https://featuredrop.dev/docs/components/gallery) | Live interactive demos |
385
+ | [Playground](https://featuredrop.dev/playground) | Local sandbox + hosted templates |
386
+ | [API Reference](https://featuredrop.dev/docs/api) | All functions, hooks, and components |
387
+ | [Migration Guide](https://featuredrop.dev/docs/migration) | Migrate from Beamer, Pendo, Headway |
388
+ | [Architecture](https://featuredrop.dev/docs/concepts/architecture) | Three-check algorithm, cross-device sync |
389
+ | [Recipes](https://featuredrop.dev/docs/recipes) | Copy-paste integration patterns |
390
+ | [Frameworks](https://featuredrop.dev/docs/frameworks/vue) | Vue, Svelte, Solid, Angular, Preact, Web Components |
828
391
 
829
- Read the full [Architecture doc](docs/ARCHITECTURE.md) for cross-device sync flow and custom adapter patterns.
392
+ ---
830
393
 
831
- ## Comparison
394
+ ## Branding Assets
832
395
 
833
- | | featuredrop | Beamer | Headway | AnnounceKit | Canny |
834
- |---|:---:|:---:|:---:|:---:|:---:|
835
- | **Price** | **Free** | $59-399/mo | $49-249/mo | $79-299/mo | $79+ |
836
- | Auto-expiring badges | Yes | - | - | - | - |
837
- | Changelog widget | Yes | Yes | Yes | Yes | Yes |
838
- | Product tours | Yes | - | - | - | - |
839
- | Onboarding checklists | Yes | - | - | - | - |
840
- | Spotlight/beacon | Yes | - | - | - | - |
841
- | Hotspot tooltips | Yes | - | - | - | - |
842
- | Announcement modal | Yes | - | - | - | - |
843
- | Spotlight chaining | Yes | - | - | - | - |
844
- | Toast notifications | Yes | - | - | - | - |
845
- | Announcement banner | Yes | - | - | - | - |
846
- | Tab title notification | Yes | - | - | - | - |
847
- | Zero dependencies | Yes | - | - | - | - |
848
- | Framework agnostic | Yes | - | - | - | - |
849
- | React bindings | Yes | - | - | - | - |
850
- | Headless mode | Yes | - | - | - | - |
851
- | Cross-device sync | Yes | Yes | Yes | Yes | Yes |
852
- | Per-feature dismiss | Yes | - | - | - | - |
853
- | Scheduled publishing | Yes | Yes | Yes | Yes | - |
854
- | Priority levels | Yes | - | - | - | - |
855
- | Analytics callbacks | Yes | Built-in | Built-in | Built-in | Built-in |
856
- | < 10 kB minzipped | Yes | - | - | - | - |
857
- | Self-hosted | Yes | - | - | - | - |
858
- | Open source | Yes | - | - | - | - |
859
-
860
- ## Framework Support
396
+ All marketing assets are in [`apps/docs/public/og/`](apps/docs/public/og/).
861
397
 
862
- | Framework | Status | Import |
863
- |-----------|--------|--------|
864
- | React / Next.js | Stable | `featuredrop/react` |
865
- | Vanilla JS | Stable | `featuredrop` |
866
- | SolidJS | Preview | `featuredrop/solid` |
867
- | Preact (compat) | Preview | `featuredrop/preact` |
868
- | Web Components | Preview | `featuredrop/web-components` |
869
- | Angular | Preview | `featuredrop/angular` |
870
- | Vue 3 | Preview | `featuredrop/vue` |
871
- | Svelte 5 | Preview (store bindings) | `featuredrop/svelte` |
398
+ | File | Ratio | Use |
399
+ |---|---|---|
400
+ | `og.png` | 1200×630 (1.91:1) | Website OG / link previews, Discord, Slack |
401
+ | `github-social.png` | 1280×640 (2:1) | **GitHub repo social preview** ← upload this |
402
+ | `twitter-header.png` | 1500×500 (3:1) | X.com profile header |
403
+ | `linkedin-banner.png` | 1584×396 (4:1) | LinkedIn company page banner |
404
+ | `reddit-16x9.png` | 1920×1080 (16:9) | Reddit posts, r/reactjs, r/webdev |
405
+ | `producthunt.png` | 1270×760 | Product Hunt launch |
406
+ | `story-9x16.png` | 1080×1920 (9:16) | Instagram / LinkedIn Stories |
872
407
 
873
- ## Documentation
408
+ **GitHub social preview**: Repo **Settings → Social preview → Upload** `apps/docs/public/og/github-social.png`.
874
409
 
875
- - [Docs App Source](apps/docs/) — Next.js + Nextra site scaffold (`/`, `/docs`, `/playground`)
876
- - [Docs Site Guide](docs/DOCS_SITE.md) — Local commands and Vercel deployment setup
877
- - [API Reference](docs/API.md) — All functions, adapters, hooks, components
878
- - [Recipes](docs/RECIPES.md) — Copy-paste integration patterns (sidebar badge, tours, migration, A/B tests)
879
- - [Architecture](docs/ARCHITECTURE.md) — Three-check algorithm, cross-device sync, custom adapters
880
- - [Next.js Example](examples/nextjs/) — Full App Router integration
881
- - [Vanilla Example](examples/vanilla/) — Plain HTML, zero build step
882
- - [React Sandbox](examples/sandbox-react/) — Interactive local/online playground
410
+ ---
883
411
 
884
412
  ## Contributing
885
413
 
886
- See [CONTRIBUTING.md](CONTRIBUTING.md) for development setup, commit conventions, and how releases work.
414
+ See [CONTRIBUTING.md](CONTRIBUTING.md) for dev setup, commit conventions, and release process.
887
415
 
888
416
  ## Security
889
417
 
890
- - Report vulnerabilities privately via [SECURITY.md](SECURITY.md).
891
- - CI includes CodeQL static analysis on pull requests and `main`.
892
- - `pnpm security-check` scans runtime source for unsafe execution/rendering patterns.
418
+ - Report vulnerabilities privately via [SECURITY.md](SECURITY.md)
419
+ - CI includes CodeQL static analysis on PRs and `main`
420
+ - `pnpm security-check` scans runtime source for unsafe execution patterns
893
421
 
894
422
  ## License
895
423
 
896
- MIT &copy; [Glincker](https://glincker.com)
424
+ MIT © [GLINR STUDIOS](https://glincker.com)
897
425
 
898
426
  ---
899
427
 
900
428
  <p align="center">
901
- <sub>Built and battle-tested at <a href="https://askverdict.ai">AskVerdict</a>.</sub>
902
- <br />
903
- <strong>A <a href="https://glincker.com">GLINCKER</a> open source project.</strong>
429
+ <sub>Built and battle-tested at <a href="https://askverdict.ai">AskVerdict AI</a>.</sub><br />
430
+ <strong>A <a href="https://glincker.com">GLINR STUDIOS</a> open source project.</strong>
904
431
  </p>