@blakfy/cookie 2.1.1 → 2.2.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.
package/README.md CHANGED
@@ -1,480 +1,594 @@
1
- # Blakfy Cookie Widget
2
-
3
- > ![v2.1.1](https://img.shields.io/badge/version-2.1.1-3E5C3A) ![MIT](https://img.shields.io/badge/license-MIT-blue) ![size](https://img.shields.io/badge/size-%E2%89%A430KB-success) ![langs](https://img.shields.io/badge/languages-23-orange) ![presets](https://img.shields.io/badge/presets-18-purple)
4
- >
5
- > Tek script ile **KVKK + GDPR + CCPA + Google CMv2 + Microsoft UET + Yandex Metrica + IAB TCF v2.2** uyumlu cookie consent (çerez onayı) widget. **23 dil**, **18 hazır preset** (üçüncü parti araç entegrasyonu), **3 renk teması**, **3-tab tercihler modalı** (Kategoriler / Hizmetler / Hakkında), **tag-gating** (script engelleme/serbest bırakma) dahil.
6
-
7
- **Versiyon:** 2.1.1 **Lisans:** MIT **npm:** `@blakfy/cookie@2.1.1` · `@blakfy/cookie-next@2.1.1` **CDN:** `cdn.jsdelivr.net/npm/@blakfy/cookie@2.1.1`
8
-
9
- ---
10
-
11
- ## Quick Start (3 adım)
12
-
13
- ### 1. Bootstrap (head içine, ilk script olarak)
14
-
15
- `<head>` bloğuna **en üst sıraya** koy. GTM/GA4/Pixel'den **önce** yüklenmeli onların default `denied` durumunda başlamasını sağlar (Google Consent Mode v2 + Microsoft UET).
16
-
17
- ```html
18
- <!-- Bootstrap: Tüm consent sinyallerini 'denied' olarak başlatır -->
19
- <script src="https://cdn.jsdelivr.net/npm/@blakfy/cookie@2.1.1/dist/cookie-defaults.min.js"></script>
20
- ```
21
-
22
- ### 2. Site içerik script'lerin (GTM/GA4/Pixel/Clarity vb.)
23
-
24
- Bunlar olduğu gibi kalır. Bootstrap zaten consent default'larını `denied` olarak set etti, dolayısıyla bu tag'ler kullanıcı onay verene kadar cookie yazmaz.
25
-
26
- ```html
27
- <!-- Örnek: Google Tag Manager (değişiklik gerektirmez) -->
28
- <script>
29
- (function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start':
30
- new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0],
31
- j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src=
32
- 'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f);
33
- })(window,document,'script','dataLayer','GTM-XXXXXX');
34
- </script>
35
- ```
36
-
37
- ### 3. Widget (body sonu, `</body>`'den önce)
38
-
39
- ```html
40
- <script
41
- src="https://cdn.jsdelivr.net/npm/@blakfy/cookie@2.1.1/dist/cookie.min.js"
42
- data-blakfy-locale="auto"
43
- data-blakfy-policy-url="/cerez-politikasi"
44
- data-blakfy-version="1.0"
45
- data-blakfy-presets="ga4,gtm,facebook,clarity"
46
- data-blakfy-ccpa="auto"
47
- data-blakfy-tcf="false"
48
- data-blakfy-position="bottom-right"
49
- data-blakfy-theme="auto"
50
- data-blakfy-accent="#3E5C3A"></script>
51
- ```
52
-
53
- **Bittiğinde:** Sayfa yüklendiğinde sağ alt köşede consent banner ve "Powered by Blakfy Studio" badge belirir. Kullanıcı bir karar verene kadar GTM/GA4/Facebook Pixel/Clarity tag'leri çalışmaz; karar verildiğinde otomatik aktif olur.
54
-
55
- ---
56
-
57
- ## Senaryolar
58
-
59
- ### Vanilla HTML / WordPress / Shopify
60
-
61
- Yukarıdaki 3 adımı `<head>` ve `</body>` öncesine yapıştır. Çalışır. Tam çalışan örnek için: [`examples/vanilla-html.html`](./examples/vanilla-html.html).
62
-
63
- WordPress için PHP snippet: [`examples/wordpress-snippet.php`](./examples/wordpress-snippet.php).
64
-
65
- Shopify için: theme `theme.liquid` dosyasının `<head>` ve `</body>` öncesi noktalarına aynı 3 adım eklenir. Ek bir paket gerekmez.
66
-
67
- ### Next.js 14+ (önerilen App Router)
68
-
69
- ```bash
70
- npm install @blakfy/cookie-next@2
71
- ```
72
-
73
- `app/layout.tsx`:
74
-
75
- ```tsx
76
- import { BlakfyCookieProvider, ConsentModeDefault } from "@blakfy/cookie-next";
77
-
78
- export default function RootLayout({ children }: { children: React.ReactNode }) {
79
- return (
80
- <html lang="tr">
81
- <head>
82
- {/* Bootstrap: GCM/UET/Yandex default denied */}
83
- <ConsentModeDefault />
84
- </head>
85
- <body>
86
- <BlakfyCookieProvider
87
- locale="auto"
88
- policyUrl="/cerez-politikasi"
89
- policyVersion="1.0"
90
- presets="ga4,gtm,facebook,clarity"
91
- ccpa="auto"
92
- tcf={false}
93
- position="bottom-right"
94
- theme="auto"
95
- accent="#3E5C3A"
96
- >
97
- {children}
98
- </BlakfyCookieProvider>
99
- </body>
100
- </html>
101
- );
102
- }
103
- ```
104
-
105
- Tam Next.js örneği: [`examples/nextjs/`](./examples/nextjs/).
106
- Paket README'si: [`packages/cookie-next/README.md`](./packages/cookie-next/README.md).
107
-
108
- ### GTM ile birlikte
109
-
110
- GTM, default `denied` ile başlar (bootstrap sayesinde). Trigger'larında "Consent State" şartı eklemen yeterli — Tag → "Consent settings" → "Require additional consent for tag firing" → ilgili kategoriler (örn. `analytics_storage` veya `ad_storage`). Kullanıcı kabul edince `gtag('consent','update', ...)` otomatik çağrılır ve tag'ler kendiliğinden tetiklenir.
111
-
112
- ```js
113
- // Doğrulama (DevTools console)
114
- window.dataLayer.find(e => e[0] === 'consent');
115
- // ["consent", "default", { ad_storage: "denied", ... }]
116
- window.BlakfyCookie.acceptAll();
117
- // → ["consent", "update", { ad_storage: "granted", ... }]
118
- ```
119
-
120
- ### Bing Ads (Microsoft UET) ile
121
-
122
- UET tag'ini olduğu gibi koy — bootstrap `window.uetq.push('consent','default',{ad_storage:'denied'})` çağrısını otomatik yapar. Kullanıcı `marketing` onayı verince UET'e `update` push edilir. Microsoft Clarity de aynı sinyali okur.
123
-
124
- ```html
125
- <!-- UET tag — değişiklik gerektirmez -->
126
- <script>
127
- (function(w,d,t,r,u){var f,n,i;w[u]=w[u]||[],f=function(){var o={ti:"XXXXXXX"};
128
- o.q=w[u],w[u]=new UET(o),w[u].push("pageLoad")},n=d.createElement(t),n.src=r,
129
- n.async=1,n.onload=n.onreadystatechange=function(){var s=this.readyState;
130
- s&&s!=="loaded"&&s!=="complete"||(f(),n.onload=n.onreadystatechange=null)},
131
- i=d.getElementsByTagName(t)[0],i.parentNode.insertBefore(n,i);
132
- })(window,document,"script","//bat.bing.com/bat.js","uetq");
133
- </script>
134
- ```
135
-
136
- ### Yandex Metrica ile
137
-
138
- Yandex'in standart consent API'si yoktur, tag-gating kullanılır. Metrica embed kodunu `type="text/plain"` ile sar:
139
-
140
- ```html
141
- <script type="text/plain" data-blakfy-category="analytics">
142
- (function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
143
- m[i].l=1*new Date();k=e.createElement(t),a=e.getElementsByTagName(t)[0],
144
- k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
145
- (window,document,"script","https://mc.yandex.ru/metrica/tag.js","ym");
146
- ym(XXXXXXXX, "init", { clickmap:true, trackLinks:true, accurateTrackBitrate:true });
147
- </script>
148
- ```
149
-
150
- **Webvisor (session replay)** için ayrı `recording` kategori onayı gerekir (KVKK/GDPR yorumu). Otomatik temizlenen cookie'ler: `_ym_*`, `yandexuid`, `yabs-frequency`.
151
-
152
- ---
153
-
154
- ## Tercihler Modalı (3 Sekme)
155
-
156
- "Tercihler" veya `window.BlakfyCookie.open()` çağrıldığında açılan modal 3 sekmeden oluşur:
157
-
158
- | Sekme | İçerik |
159
- |---|---|
160
- | **Kategoriler** | Zorunlu / Analitik / Pazarlama / Fonksiyonel toggle switch'leri. "Seçimleri Kaydet" ve "Tümünü Kabul Et" butonları. |
161
- | **Hizmetler** | Aktif preset'lere göre accordion kartlar. Her kart: açıklama, veri işleyici, adres, amaçlar, teknolojiler, toplanan veriler, hukuki dayanak, saklama süresi, aktarım ülkeleri, gizlilik politikası linki. GDPR Madde 13/14 + KVKK Madde 10 zorunlu bilgi ifşası. |
162
- | **Hakkında** | CMP kimliği (Blakfy Studio), platform açıklaması (GDPR / KVKK / CCPA), sürüm numarası. |
163
-
164
- > **Yasal dayanak:** Hizmetler sekmesindeki bilgiler GDPR Madde 13/14 (bilgi yükümlülüğü) ve KVKK Madde 10 (aydınlatma yükümlülüğü) gerekliliklerini karşılar. Presets kullanmadan da her site kendi hizmet bilgilerini `SERVICE_METADATA` yapısına ekleyebilir.
165
-
166
- ---
167
-
168
- ## Renk Temaları
169
-
170
- 3 hazır tema, `data-blakfy-theme` ile seçilir:
171
-
172
- | Tema | Açıklama | Attribute değeri |
173
- |---|---|---|
174
- | **Beyaz** (varsayılan) | Beyaz arka plan, koyu metin | `light` veya atlanabilir |
175
- | **Açık gri** | `#f0f0f0` arka plan, koyu metin | `gray` |
176
- | **Siyah** | `#1a1a1a` arka plan, açık metin | `dark` |
177
- | **Otomatik** | Sistem `prefers-color-scheme` ayarını okur | `auto` |
178
-
179
- ```html
180
- <!-- Açık gri tema -->
181
- <script src="...cookie.min.js" data-blakfy-theme="gray" ...></script>
182
-
183
- <!-- Siyah tema -->
184
- <script src="...cookie.min.js" data-blakfy-theme="dark" ...></script>
185
-
186
- <!-- Sistem temasını takip et -->
187
- <script src="...cookie.min.js" data-blakfy-theme="auto" ...></script>
188
- ```
189
-
190
- Tüm temalarda `--blakfy-accent` CSS değişkeni geçerliliğini korur buton ve aktif sekme rengi `data-blakfy-accent` ile özelleştirilebilir.
191
-
192
- ---
193
-
194
- ## Configuration Reference
195
-
196
- Tüm `<script>` tag'i üzerine konabilen `data-blakfy-*` attribute'ları:
197
-
198
- | Attribute | Default | Tip | Açıklama |
199
- |---|---|---|---|
200
- | `data-blakfy-locale` | `auto` | BCP47 ya da `auto` | Dil. `auto` → tarayıcıdan tespit. 23 desteklenen dil aşağıda. |
201
- | `data-blakfy-main-lang` | `null` | BCP47 | Sitenin **birincil dili** (audit log için). Boşsa locale ile aynı. |
202
- | `data-blakfy-policy-url` | `/cerez-politikasi` | URL | "Çerez Politikası" linki için hedef. |
203
- | `data-blakfy-version` | `1.0` | string | **Politika versiyonu**. Bunu artırırsan tüm kullanıcılar tekrar onay vermek zorunda (re-consent). |
204
- | `data-blakfy-audit-endpoint` | `null` | URL | Consent kararı bu endpoint'e POST edilir (KVKK Md.12 / GDPR Art.7(1) kanıt). |
205
- | `data-blakfy-position` | `bottom-right` | enum | `bottom-right` \| `bottom-left` \| `bottom` \| `top` \| `center` |
206
- | `data-blakfy-theme` | `auto` | enum | **`light`** (beyaz) \| **`gray`** (açık gri) \| **`dark`** (siyah) \| `auto` (`prefers-color-scheme`) |
207
- | `data-blakfy-accent` | `#3E5C3A` | hex | Buton ve vurgu rengi. |
208
- | `data-blakfy-presets` | `null` | virgül listesi | Etkinleştirilecek preset key'leri. Örn: `ga4,gtm,facebook,clarity`. Aşağıda 18 preset listesi. |
209
- | `data-blakfy-tcf` | `false` | bool | IAB TCF v2.2 modülünü aç (`__tcfapi` global). |
210
- | `data-blakfy-cmp-id` | `0` | int | TCF CMP ID. `0` = preview/test mode. Sertifikasyon sonrası gerçek ID. |
211
- | `data-blakfy-ccpa` | `auto` | enum | `auto` (jurisdiction tespiti), `true` (her zaman aç), `false` (kapat) |
212
- | `data-blakfy-gpc` | `respect` | enum | `respect` (tarayıcı GPC'si auto-deny say) \| `ignore` |
213
- | `data-blakfy-dnt` | `respect` | enum | `respect` (UI'da uyar) \| `auto-deny` (auto-reject) |
214
- | `data-blakfy-status-url` | jsDelivr `@2/status.json` | URL | Status bar mesajları için kaynak. |
215
- | `data-blakfy-status` | `true` | bool | `false` ise status bar render edilmez. |
216
-
217
- **Desteklenen 23 dil:** `tr`, `en`, `ar`, `fa`, `ur`, `fr`, `ru`, `de`, `he`, `uk`, `es`, `it`, `pt`, `nl`, `pl`, `sv`, `cs`, `zh`, `zh-TW`, `ja`, `ko`, `id`, `hi` (RTL: `ar`, `fa`, `ur`, `he`).
218
-
219
- ---
220
-
221
- ## API Reference
222
-
223
- `window.BlakfyCookie` global olarak erişilebilir. Tüm v1 metodları korunmuştur, v2'de yeni metodlar eklendi.
224
-
225
- | Metod | Sürüm | İmza | Açıklama |
226
- |---|---|---|---|
227
- | `version` | v1 | `string` | Kütüphane sürümü, örn. `"2.1.0"`. |
228
- | `open()` | v1 | `() => void` | Tercihler modalını aç. |
229
- | `acceptAll()` | v1 | `() => void` | Tüm kategorileri kabul et. |
230
- | `rejectAll()` | v1 | `() => void` | Tüm kategorileri reddet (essential dışında). |
231
- | `getConsent(cat)` | v1 | `(c: ConsentCategory) => boolean` | Kategori onaylı mı. `essential` her zaman `true`. |
232
- | `getState()` | v1 | `() => BlakfyConsentState \| null` | Tam consent state objesi. |
233
- | `onChange(fn)` | v1 | `(fn: (state) => void) => void` | State değişince çağrılır. |
234
- | `setLocale(loc)` | v1 | `(loc: BlakfyLocale) => void` | Dili değiştir (UI re-render). |
235
- | `getMainLang()` | v1 | `() => BlakfyLocale` | Audit log için ayarlanan birincil dil. |
236
- | `onConsent(cat, fn)` | **v2** | `(c, fn: (granted: boolean) => void) => void` | Kategori-bazlı listener. Mevcut state ile **anında** çağrılır, sonra her değişimde tekrar. |
237
- | `registerCleanup(opts)` | **v2** | `(opts: { category, cookies?, storage? }) => void` | Onay geri çekildiğinde silinecek cookie'leri/localStorage anahtarlarını kaydet. |
238
- | `unblock(cat)` | **v2** | `(c: ConsentCategory) => void` | Kategori için tag-gating'i manuel aç. |
239
- | `scan()` | **v2** | `() => ConsentCategory[]` | DOM'u tekrar tara (SPA navigasyonu sonrası). |
240
- | `usePreset(name)` | **v2** | `(name: string) => Preset \| null` | Hazır preset'i runtime'da uygula. |
241
- | `tcf.getTCString()` | **v2** | `() => string` | IAB TCF v2.2 TC string. |
242
- | `ccpa.optOut()` | **v2** | `() => void` | CCPA "Do Not Sell" opt-out. |
243
- | `ccpa.isOptedOut()` | **v2** | `() => boolean` | CCPA opt-out durumu. |
244
- | `getJurisdiction()` | **v2** | `() => "GDPR" \| "CCPA" \| "LGPD" \| "default"` | Tespit edilen yetki alanı. |
245
- | `window.__tcfapi(...)` | **v2** | IAB standart | `getTCData`, `addEventListener`, `removeEventListener` (TCF v2.2 spec). |
246
-
247
- **ConsentCategory:** `"essential"` | `"analytics"` | `"marketing"` | `"functional"` | `"recording"`
248
-
249
- **BlakfyConsentState alanları:** `id` (uuid), `essential` (true), `analytics`, `marketing`, `functional`, `recording`, `timestamp` (ISO), `version` (policy), `locale`, `mainLang`, `jurisdiction`, `tcString`, `uspString`.
250
-
251
- ---
252
-
253
- ## Tag-Gating
254
-
255
- Üçüncü parti tag'lerini kullanıcı onay vermeden çalıştırma. 4 yöntem.
256
-
257
- ### Harici script
258
-
259
- ```html
260
- <!-- type="text/plain" tarayıcı çalıştırmaz -->
261
- <script type="text/plain"
262
- data-blakfy-category="marketing"
263
- data-blakfy-src="https://connect.facebook.net/en_US/fbevents.js"
264
- async></script>
265
- ```
266
-
267
- Onay verildiğinde widget script'in `type` attribute'unu `text/javascript`'e çevirir, `data-blakfy-src` → `src` taşır ve DOM'a yeniden ekler.
268
-
269
- ### Inline script
270
-
271
- ```html
272
- <script type="text/plain" data-blakfy-category="analytics">
273
- console.log("Analytics tag fired");
274
- fbq('init', 'PIXEL_ID');
275
- </script>
276
- ```
277
-
278
- ### Iframe (YouTube, Vimeo, harita, vb.)
279
-
280
- ```html
281
- <iframe data-blakfy-src="https://www.youtube.com/embed/VIDEO_ID"
282
- data-blakfy-category="marketing"
283
- data-blakfy-placeholder="auto"
284
- width="560" height="315"
285
- title="YouTube video"
286
- allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope"
287
- allowfullscreen></iframe>
288
- ```
289
-
290
- `data-blakfy-placeholder="auto"` otomatik "Çerez onayı bekleniyor" placeholder UI'ı gösterilir.
291
-
292
- ### React/Next.js declarative gating
293
-
294
- ```tsx
295
- "use client";
296
- import { useGating } from "@blakfy/cookie-next";
297
-
298
- export function YouTubeEmbed({ id }: { id: string }) {
299
- const allowed = useGating("marketing");
300
- if (!allowed) {
301
- return (
302
- <div className="aspect-video rounded bg-zinc-100 flex items-center justify-center">
303
- Onay bekleniyor bu içeriği görmek için pazarlama çerezlerini kabul edin.
304
- </div>
305
- );
306
- }
307
- return (
308
- <iframe
309
- src={`https://www.youtube.com/embed/${id}`}
310
- className="w-full aspect-video"
311
- title="YouTube"
312
- allow="accelerometer; clipboard-write; encrypted-media; gyroscope"
313
- allowFullScreen
314
- />
315
- );
316
- }
317
- ```
318
-
319
- ---
320
-
321
- ## 18 Hazır Preset
322
-
323
- `data-blakfy-presets="ga4,gtm,facebook,clarity"` gibi virgüllü liste ile aktive edilir. Preset; cookie'leri ve localStorage anahtarlarını otomatik **registerCleanup**'a kaydeder, böylece kullanıcı onayı geri çekince bunlar otomatik silinir.
324
-
325
- | Key | Araç | Kategori | Örnek Cookie / Storage |
326
- |---|---|---|---|
327
- | `ga4` | Google Analytics 4 | analytics | `_ga`, `_ga_*`, `_gid`, `_gat` |
328
- | `gtm` | Google Tag Manager | analytics | `_dc_gtm_*` |
329
- | `maps` | Google Maps | functional | `NID`, `SID`, `HSID` |
330
- | `recaptcha` | Google reCAPTCHA | functional | `_GRECAPTCHA` |
331
- | `facebook` | Facebook Pixel | marketing | `_fbp`, `_fbc`, `fr` |
332
- | `youtube` | YouTube embed | marketing | `VISITOR_INFO1_LIVE`, `YSC`, `PREF` |
333
- | `vimeo` | Vimeo embed | marketing | `vuid`, `_ga` |
334
- | `hotjar` | Hotjar | analytics | `_hjSession*`, `_hjid` |
335
- | `clarity` | Microsoft Clarity | analytics | `_clck`, `_clsk`, `MUID` |
336
- | `linkedin` | LinkedIn Insight | marketing | `li_sugr`, `bcookie`, `lidc`, `UserMatchHistory` |
337
- | `yandex` | Yandex Metrica | analytics + recording | `_ym_*`, `yandexuid`, `yabs-frequency` |
338
- | `bing` | Bing Ads (UET) | marketing | `MUID`, `_uetsid`, `_uetvid` |
339
- | `tiktok` | TikTok Pixel | marketing | `tt_*`, `_ttp` |
340
- | `pinterest` | Pinterest Tag | marketing | `_pinterest_*`, `_pin_unauth` |
341
- | `tawkto` | Tawk.to chat | functional | `__tawkUUID`, `Tawk_*` |
342
- | `intercom` | Intercom | functional | `intercom-*` |
343
- | `hubspot` | HubSpot | marketing | `__hssc`, `__hssrc`, `__hstc`, `hubspotutk` |
344
- | `mailchimp` | Mailchimp | marketing | `_mcid`, `mc_*` |
345
-
346
- **Manuel ekleme** (preset'sız):
347
-
348
- ```js
349
- window.BlakfyCookie.registerCleanup({
350
- category: "marketing",
351
- cookies: ["my_custom_pixel", /^_track_/],
352
- storage: ["myAppMarketingCache"]
353
- });
354
- ```
355
-
356
- ---
357
-
358
- ## Compliance
359
-
360
- | Yasa / Standart | Yetki Alanı | Modül | Ne yapar |
361
- |---|---|---|---|
362
- | **GDPR** | AB | `core/` + `ui/` | Eşit prominence kabul/red butonu, pre-tick yok, granular kategori onayı. |
363
- | **ePrivacy Directive** | AB | `core/consent-store` | Onay öncesi cookie yazılmaz. |
364
- | **KVKK** | Türkiye | `core/audit.js` | Md.12 ispat yükümlülüğü için audit log POST. |
365
- | **CCPA / CPRA** | Kaliforniya | `compliance/ccpa.js` | "Do Not Sell" linki, USP string `1YYY`, GPC saygısı. |
366
- | **LGPD** | Brezilya | `i18n/pt.js` + `geo/` | Portekizce UI, jurisdiction tespiti (v2.1'de tam destek). |
367
- | **Google Consent Mode v2** | Google ekosistemi | `compliance/google-cmv2.js` | `gtag('consent','update', {...})` 7 sinyal. |
368
- | **Microsoft UET** | Bing Ads + Clarity | `compliance/microsoft-uet.js` | `uetq.push('consent','update', {ad_storage})` |
369
- | **Yandex Metrica** | Yandex ekosistemi | `compliance/yandex-metrica.js` | Tag-gating + Webvisor ayrı `recording` kategori. |
370
- | **IAB TCF v2.2** | AB AdTech (AdSense) | `compliance/tcf-v2.js` | `__tcfapi`, TC string, GVL fetch. |
371
- | **GPC** | Tarayıcı standardı | `compliance/gpc.js` | `navigator.globalPrivacyControl` → marketing/analytics auto-deny. |
372
- | **DNT** | Tarayıcı standardı | `compliance/dnt.js` | `navigator.doNotTrack === "1"` → UI uyarı veya auto-deny. |
373
-
374
- ### TCF v2.2 (AdSense / Ad Manager kullanıcıları için)
375
-
376
- `data-blakfy-tcf="true" data-blakfy-cmp-id="0"` ile preview mode'da çalışır. Production için IAB Europe sertifikasyonu gerekir (~2-3 ay, yıllık ~€2.000 ücret). Süreç ve audit gereksinimleri: [`TCF-CERTIFICATION.md`](./TCF-CERTIFICATION.md).
377
-
378
- ```js
379
- // TC string okuma
380
- window.BlakfyCookie.tcf.getTCString();
381
- // IAB standart API
382
- window.__tcfapi("getTCData", 2, (data, success) => console.log(data));
383
- ```
384
-
385
- ### CCPA / CPRA (ABD trafiği)
386
-
387
- `data-blakfy-ccpa="auto"` → `geo/jurisdiction.js` Kaliforniya tespit ederse:
388
-
389
- - Banner'daki "Reddet" **"Do Not Sell or Share My Personal Information"**
390
- - Footer'a kalıcı `<a class="blakfy-ccpa-link">` (yasal zorunluluk)
391
- - USP string `1YYY` (versiyon, opt-out, sale, third-party) `__uspapi` üzerinden expose edilir
392
- - `Sec-GPC: 1` header otomatik opt-out sayılır
393
-
394
- ### GPC / DNT default davranışı
395
-
396
- - **GPC** (`navigator.globalPrivacyControl === true`): Kullanıcı açık onay vermediyse `marketing` ve `analytics` otomatik **denied**. CCPA jurisdiction'da yasal opt-out.
397
- - **DNT** (`navigator.doNotTrack === "1"`): Default'ta sadece banner'da uyarı yazısı. `data-blakfy-dnt="auto-deny"` ile auto-reject yapılabilir. (DNT zayıf bir standart; GPC tercih edilir.)
398
-
399
- ---
400
-
401
- ## Troubleshooting
402
-
403
- ### Widget hiç görünmüyor
404
-
405
- 1. Console'da hata var mı? Network sekmesinde `cookie.min.js` 200 dönüyor mu?
406
- 2. Script tag'i `</body>`'den önce mi? `<head>` içine konursa DOM hazır olmadan render denenir.
407
- 3. Daha önce kabul/red verildiyse banner görünmez (kalıcı). Modal'ı tetiklemek için: `window.BlakfyCookie.open()` veya footer'a "Çerez Tercihleri" linki koy.
408
-
409
- ### Banner her sayfada / her seferinde tekrar açılıyor
410
-
411
- `data-blakfy-version` (politika versiyonu) değişti mi? Bu değer her cookie'de saklanır; uyuşmuyorsa re-consent tetiklenir. **Dikkat:** Kütüphane sürümü `cookie.min.js` patch güncellemeleri kullanıcıyı **etkilemez** (v2 düzeltmesi). v1'de bu bir bug'dı.
412
-
413
- ### GTM / GA4 tetiklenmiyor
414
-
415
- - **Bootstrap script GTM'den önce mi yükleniyor?** `cookie-defaults.min.js` `<head>`'in **ilk** script'i olmalı. Aksi halde GTM `consent default` çağrısını kaçırır.
416
- - Tag "Consent settings" "Require additional consent for tag firing" doğru kategorilere ayarlı mı?
417
- - Kullanıcı kabul etti mi: `window.BlakfyCookie.getState()` ile kontrol et.
418
-
419
- ### Yandex Metrica çalışıyor ama Webvisor (session replay) çalışmıyor
420
-
421
- Webvisor `marketing` değil, **`recording`** kategorisi gerektirir. Modal'da bu seçeneğe ayrı onay vermelisin. KVKK/GDPR yorumu: session replay biyometriye yakın özel veri olduğu için ek granular onay zorunlu.
422
-
423
- ### TCF preview mode'da takıldım
424
-
425
- `data-blakfy-cmp-id="0"` preview mode'dur. AdSense/Ad Manager production için IAB Europe sertifikasyonu sonrası atanan gerçek CMP ID girilmeli. Detay: [`TCF-CERTIFICATION.md`](./TCF-CERTIFICATION.md).
426
-
427
- ### "Powered by Blakfy Studio" badge'i nasıl gizlerim?
428
-
429
- **Gizlenemez.** 3 katmanlı anti-tampering korumalı (CSS `!important`, MutationObserver re-injection, kod-baked HTML). Lisans şartının parçasıdır. Marka koruması.
430
-
431
- ### Next.js'te FOUC (içerik atlaması) görüyorum
432
-
433
- `@blakfy/cookie-next@2`'ye yükselt. v1'de FOUC vardı; v2'de `next/script` `beforeInteractive` strateji kullanılıyor.
434
-
435
- ### `acceptAll()` sonrası 3rd-party tag'ler hala engelli
436
-
437
- `window.BlakfyCookie.scan()` çağır SPA navigasyonu veya dinamik DOM ekleme sonrası gating observer'ı tekrar tetikler.
438
-
439
- ---
440
-
441
- ## Migration v1 → v2
442
-
443
- **Kısa cevap:** Değişiklik gerekmez. v1 kullanıcıları `@1` etiketinde kalır, çalışmaya devam eder. v2'ye opt-in yapmak için **CDN URL'sindeki `@1` → `@2`** ve dosya adı `cookie.js` → `dist/cookie.min.js` güncellemesi yeterli — tüm v1 attribute'ları korunur, kullanıcı consent cookie'si geriye uyumlu okunur.
444
-
445
- Detay: [`MIGRATION.md`](./MIGRATION.md).
446
-
447
- ---
448
-
449
- ## Footer "Çerez Tercihleri" linki
450
-
451
- ```html
452
- <a href="#" onclick="event.preventDefault(); window.BlakfyCookie.open();">
453
- Çerez Tercihleri
454
- </a>
455
- ```
456
-
457
- Veya React:
458
-
459
- ```tsx
460
- <button onClick={() => window.BlakfyCookie?.open()}>Çerez Tercihleri</button>
461
- ```
462
-
463
- ---
464
-
465
- ## Diğer Kaynaklar
466
-
467
- - [`ARCHITECTURE.md`](./ARCHITECTURE.md) modül yapısı, veri akışı, boyut bütçesi
468
- - [`COMPLIANCE.md`](./COMPLIANCE.md) — her yasa için detaylı uyumluluk mappingleri
469
- - [`CHANGELOG.md`](./CHANGELOG.md) sürüm notları
470
- - [`MIGRATION.md`](./MIGRATION.md) — v1 → v2 geçiş
471
- - [`TCF-CERTIFICATION.md`](./TCF-CERTIFICATION.md) IAB Europe başvuru süreci
472
- - [`packages/cookie-next/README.md`](./packages/cookie-next/README.md) npm paketi rehberi
473
-
474
- ---
475
-
476
- ## License
477
-
478
- MIT © Blakfy Studio. "Powered by Blakfy Studio" branding badge anti-tampering korumalı ve kaldırılamaz.
479
-
480
- Issues: https://github.com/tariktunc/blakfy-cookie/issues
1
+ # Blakfy Cookie Widget
2
+
3
+ > [![CI](https://github.com/tariktunc/blakfy-cookie/actions/workflows/test.yml/badge.svg)](https://github.com/tariktunc/blakfy-cookie/actions/workflows/test.yml) [![npm version](https://img.shields.io/npm/v/@blakfy/cookie.svg)](https://www.npmjs.com/package/@blakfy/cookie) [![npm downloads](https://img.shields.io/npm/dm/@blakfy/cookie.svg)](https://www.npmjs.com/package/@blakfy/cookie) ![MIT](https://img.shields.io/badge/license-MIT-blue) ![size](https://img.shields.io/badge/size-30.2KB-success) ![tests](https://img.shields.io/badge/tests-365-success) ![langs](https://img.shields.io/badge/languages-23-orange) ![presets](https://img.shields.io/badge/presets-18-purple)
4
+ >
5
+ > Tek script ile **KVKK + GDPR + CCPA + Google CMv2 + Microsoft UET + Yandex Metrica + IAB TCF v2.2** uyumlu cookie consent (çerez onayı) widget. **23 dil**, **18 hazır preset** (üçüncü parti araç entegrasyonu), **3 renk teması**, **3-tab tercihler modalı** (Kategoriler / Hizmetler / Hakkında), **tag-gating** (script engelleme/serbest bırakma) dahil.
6
+
7
+ **Versiyon:** 2.2.0 **Lisans:** MIT **npm:** `@blakfy/cookie@2.2.0` · `@blakfy/cookie-next@2.2.0` **CDN:** `cdn.jsdelivr.net/npm/@blakfy/cookie@2.2.0`
8
+
9
+ ---
10
+
11
+ ## Installation
12
+
13
+ İki yol var. Birini seç.
14
+
15
+ ### A) CDN (önerilensıfır konfigürasyon)
16
+
17
+ | Strateji | URL | Ne zaman kullan |
18
+ | --------------------------- | -------------------------------------------------------------- | ----------------------------------------------------------------------------- |
19
+ | **Pinned** (sabit sürüm) | `cdn.jsdelivr.net/npm/@blakfy/cookie@2.2.0/dist/cookie.min.js` | Production — değişikliklerin gözden geçirilerek kabul edilmesini istersen |
20
+ | **Auto-patch** (semver tag) | `cdn.jsdelivr.net/npm/@blakfy/cookie@2/dist/cookie.min.js` | Otomatik güvenlik/patch güncellemeleri — major (`@3`) gelene kadar takip eder |
21
+
22
+ unpkg da çalışır: `unpkg.com/@blakfy/cookie@2.2.0/dist/cookie.min.js`.
23
+
24
+ ### B) npm / bundler (Vite, Webpack, Rollup, Astro)
25
+
26
+ ```bash
27
+ npm i @blakfy/cookie # Vanilla / bundler için widget
28
+ npm i @blakfy/cookie-next # Next.js / React wrapper (cookie'yi peer olarak çekmez, sadece wrapper)
29
+ ```
30
+
31
+ ```js
32
+ // app.js veya main.js
33
+ import "@blakfy/cookie";
34
+ ```
35
+
36
+ Bundler ile gelen versiyonu kontrol etmek için: `import { version } from "@blakfy/cookie"` mevcut değil — runtime'da `window.BlakfyCookie.version`.
37
+
38
+ > 💡 **Public API:** `window.BlakfyCookie` global'i hem CDN hem npm install yöntemiyle aynı şekilde tanımlanır. Aşağıdaki Quick Start CDN örneğindedir; npm install ediyorsan `<script src=...>` yerine `import "@blakfy/cookie"` kullan, gerisi aynı.
39
+
40
+ ---
41
+
42
+ ## Quick Start (3 adım)
43
+
44
+ ### 1. Bootstrap (head içine, ilk script olarak)
45
+
46
+ `<head>` bloğuna **en üst sıraya** koy. GTM/GA4/Pixel'den **önce** yüklenmeli — onların default `denied` durumunda başlamasını sağlar (Google Consent Mode v2 + Microsoft UET).
47
+
48
+ ```html
49
+ <!-- Bootstrap: Tüm consent sinyallerini 'denied' olarak başlatır -->
50
+ <script src="https://cdn.jsdelivr.net/npm/@blakfy/cookie@2.2.0/dist/cookie-defaults.min.js"></script>
51
+ ```
52
+
53
+ ### 2. Site içerik script'lerin (GTM/GA4/Pixel/Clarity vb.)
54
+
55
+ Bunlar olduğu gibi kalır. Bootstrap zaten consent default'larını `denied` olarak set etti, dolayısıyla bu tag'ler kullanıcı onay verene kadar cookie yazmaz.
56
+
57
+ ```html
58
+ <!-- Örnek: Google Tag Manager (değişiklik gerektirmez) -->
59
+ <script>
60
+ (function (w, d, s, l, i) {
61
+ w[l] = w[l] || [];
62
+ w[l].push({ "gtm.start": new Date().getTime(), event: "gtm.js" });
63
+ var f = d.getElementsByTagName(s)[0],
64
+ j = d.createElement(s),
65
+ dl = l != "dataLayer" ? "&l=" + l : "";
66
+ j.async = true;
67
+ j.src = "https://www.googletagmanager.com/gtm.js?id=" + i + dl;
68
+ f.parentNode.insertBefore(j, f);
69
+ })(window, document, "script", "dataLayer", "GTM-XXXXXX");
70
+ </script>
71
+ ```
72
+
73
+ ### 3. Widget (body sonu, `</body>`'den önce)
74
+
75
+ ```html
76
+ <script
77
+ src="https://cdn.jsdelivr.net/npm/@blakfy/cookie@2.2.0/dist/cookie.min.js"
78
+ data-blakfy-locale="auto"
79
+ data-blakfy-policy-url="/cerez-politikasi"
80
+ data-blakfy-version="1.0"
81
+ data-blakfy-presets="ga4,gtm,facebook,clarity"
82
+ data-blakfy-ccpa="auto"
83
+ data-blakfy-tcf="false"
84
+ data-blakfy-theme="auto"
85
+ data-blakfy-accent="#3E5C3A"
86
+ ></script>
87
+ <!-- Konum varsayılanı: alt-orta (bottom-center). İstersen data-blakfy-position="bottom-right" / "top-center" vb. ile değiştir. -->
88
+ ```
89
+
90
+ **Bittiğinde:** Sayfa yüklendiğinde **alt-orta**'da (varsayılan) consent banner ve "Powered by Blakfy Studio" badge belirir. Konumu değiştirmek için `data-blakfy-position` ile `bottom-right`, `bottom-left`, `top-center`, `top-right`, `top-left` seçeneklerinden birini ver. Kullanıcı bir karar verene kadar GTM/GA4/Facebook Pixel/Clarity tag'leri çalışmaz; karar verildiğinde otomatik aktif olur.
91
+
92
+ ---
93
+
94
+ ## Senaryolar
95
+
96
+ ### Vanilla HTML / WordPress / Shopify
97
+
98
+ Yukarıdaki 3 adımı `<head>` ve `</body>` öncesine yapıştır. Çalışır. Tam çalışan örnek için: [`examples/vanilla-html.html`](./examples/vanilla-html.html).
99
+
100
+ WordPress için PHP snippet: [`examples/wordpress-snippet.php`](./examples/wordpress-snippet.php).
101
+
102
+ Shopify için: theme `theme.liquid` dosyasının `<head>` ve `</body>` öncesi noktalarına aynı 3 adım eklenir. Ek bir paket gerekmez.
103
+
104
+ ### Next.js 14+ (önerilen — App Router)
105
+
106
+ ```bash
107
+ npm install @blakfy/cookie-next@2
108
+ ```
109
+
110
+ `app/layout.tsx`:
111
+
112
+ ```tsx
113
+ import { BlakfyCookieProvider, ConsentModeDefault } from "@blakfy/cookie-next";
114
+
115
+ export default function RootLayout({ children }: { children: React.ReactNode }) {
116
+ return (
117
+ <html lang="tr">
118
+ <head>
119
+ {/* Bootstrap: GCM/UET/Yandex default denied */}
120
+ <ConsentModeDefault />
121
+ </head>
122
+ <body>
123
+ <BlakfyCookieProvider
124
+ locale="auto"
125
+ policyUrl="/cerez-politikasi"
126
+ policyVersion="1.0"
127
+ presets="ga4,gtm,facebook,clarity"
128
+ ccpa="auto"
129
+ tcf={false}
130
+ // position varsayılanı "bottom-center" — değiştirmek istersen aç:
131
+ // position="bottom-right"
132
+ theme="auto"
133
+ accent="#3E5C3A"
134
+ >
135
+ {children}
136
+ </BlakfyCookieProvider>
137
+ </body>
138
+ </html>
139
+ );
140
+ }
141
+ ```
142
+
143
+ **`@blakfy/cookie-next` export'ları:**
144
+
145
+ | Export | Tip | Açıklama |
146
+ | ---------------------- | --------------- | ---------------------------------------------------------------------------------------------------- |
147
+ | `BlakfyCookieProvider` | React component | Widget'ı `next/script beforeInteractive` ile yükler. Tüm `data-blakfy-*` attribute'ları prop olarak. |
148
+ | `ConsentModeDefault` | React component | Bootstrap script'i (gtag/uetq/ym defaults `denied`). `<head>` içinde kullan. |
149
+ | `useBlakfyConsent()` | Hook | Mevcut consent state'i döndürür, değişimde re-render. |
150
+ | `useGating(category)` | Hook | Belirtilen kategori onaylı (`boolean`). Conditional render için. |
151
+ | `useTcf()` | Hook | TCF v2.2 TC string ve event listener wrapper'ı. |
152
+
153
+ Tam Next.js (App Router) örneği: [`examples/nextjs/`](./examples/nextjs/).
154
+ Paket README'si: [`packages/cookie-next/README.md`](./packages/cookie-next/README.md).
155
+
156
+ ### Next.js Pages Router (legacy projeler)
157
+
158
+ Aynı paket Pages Router ile de çalışır — provider router-agnostik. `pages/_app.tsx`:
159
+
160
+ ```tsx
161
+ import type { AppProps } from "next/app";
162
+ import { BlakfyCookieProvider, ConsentModeDefault } from "@blakfy/cookie-next";
163
+
164
+ export default function App({ Component, pageProps }: AppProps) {
165
+ return (
166
+ <>
167
+ <ConsentModeDefault />
168
+ <BlakfyCookieProvider
169
+ locale="auto"
170
+ policyUrl="/cerez-politikasi"
171
+ policyVersion="1.0"
172
+ presets="ga4,gtm,facebook,clarity"
173
+ >
174
+ <Component {...pageProps} />
175
+ </BlakfyCookieProvider>
176
+ </>
177
+ );
178
+ }
179
+ ```
180
+
181
+ `ConsentModeDefault`'u `_document.tsx`'in `<Head>` içine de koyabilirsin (daha erken yükleme). Provider `next/script beforeInteractive` ile yüklendiği için ekstra konfig gerekmez.
182
+
183
+ ### GTM ile birlikte
184
+
185
+ GTM, default `denied` ile başlar (bootstrap sayesinde). Trigger'larında "Consent State" şartı eklemen yeterli — Tag → "Consent settings" → "Require additional consent for tag firing" → ilgili kategoriler (örn. `analytics_storage` veya `ad_storage`). Kullanıcı kabul edince `gtag('consent','update', ...)` otomatik çağrılır ve tag'ler kendiliğinden tetiklenir.
186
+
187
+ ```js
188
+ // Doğrulama (DevTools console)
189
+ window.dataLayer.find((e) => e[0] === "consent");
190
+ // ["consent", "default", { ad_storage: "denied", ... }]
191
+ window.BlakfyCookie.acceptAll();
192
+ // → ["consent", "update", { ad_storage: "granted", ... }]
193
+ ```
194
+
195
+ ### Bing Ads (Microsoft UET) ile
196
+
197
+ UET tag'ini olduğu gibi koy — bootstrap `window.uetq.push('consent','default',{ad_storage:'denied'})` çağrısını otomatik yapar. Kullanıcı `marketing` onayı verince UET'e `update` push edilir. Microsoft Clarity de aynı sinyali okur.
198
+
199
+ ```html
200
+ <!-- UET tag değişiklik gerektirmez -->
201
+ <script>
202
+ (function (w, d, t, r, u) {
203
+ var f, n, i;
204
+ ((w[u] = w[u] || []),
205
+ (f = function () {
206
+ var o = { ti: "XXXXXXX" };
207
+ ((o.q = w[u]), (w[u] = new UET(o)), w[u].push("pageLoad"));
208
+ }),
209
+ (n = d.createElement(t)),
210
+ (n.src = r),
211
+ (n.async = 1),
212
+ (n.onload = n.onreadystatechange =
213
+ function () {
214
+ var s = this.readyState;
215
+ (s && s !== "loaded" && s !== "complete") ||
216
+ (f(), (n.onload = n.onreadystatechange = null));
217
+ }),
218
+ (i = d.getElementsByTagName(t)[0]),
219
+ i.parentNode.insertBefore(n, i));
220
+ })(window, document, "script", "//bat.bing.com/bat.js", "uetq");
221
+ </script>
222
+ ```
223
+
224
+ ### Yandex Metrica ile
225
+
226
+ Yandex'in standart consent API'si yoktur, tag-gating kullanılır. Metrica embed kodunu `type="text/plain"` ile sar:
227
+
228
+ ```html
229
+ <script type="text/plain" data-blakfy-category="analytics">
230
+ (function(m,e,t,r,i,k,a){m[i]=m[i]||function(){(m[i].a=m[i].a||[]).push(arguments)};
231
+ m[i].l=1*new Date();k=e.createElement(t),a=e.getElementsByTagName(t)[0],
232
+ k.async=1,k.src=r,a.parentNode.insertBefore(k,a)})
233
+ (window,document,"script","https://mc.yandex.ru/metrica/tag.js","ym");
234
+ ym(XXXXXXXX, "init", { clickmap:true, trackLinks:true, accurateTrackBitrate:true });
235
+ </script>
236
+ ```
237
+
238
+ **Webvisor (session replay)** için ayrı `recording` kategori onayı gerekir (KVKK/GDPR yorumu). Otomatik temizlenen cookie'ler: `_ym_*`, `yandexuid`, `yabs-frequency`.
239
+
240
+ ---
241
+
242
+ ## Tercihler Modalı (3 Sekme)
243
+
244
+ "Tercihler" veya `window.BlakfyCookie.open()` çağrıldığında açılan modal 3 sekmeden oluşur:
245
+
246
+ | Sekme | İçerik |
247
+ | --------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
248
+ | **Kategoriler** | Zorunlu / Analitik / Pazarlama / Fonksiyonel toggle switch'leri. "Seçimleri Kaydet" ve "Tümünü Kabul Et" butonları. |
249
+ | **Hizmetler** | Aktif preset'lere göre accordion kartlar. Her kart: açıklama, veri işleyici, adres, amaçlar, teknolojiler, toplanan veriler, hukuki dayanak, saklama süresi, aktarım ülkeleri, gizlilik politikası linki. GDPR Madde 13/14 + KVKK Madde 10 zorunlu bilgi ifşası. |
250
+ | **Hakkında** | CMP kimliği (Blakfy Studio), platform açıklaması (GDPR / KVKK / CCPA), sürüm numarası. |
251
+
252
+ > **Yasal dayanak:** Hizmetler sekmesindeki bilgiler GDPR Madde 13/14 (bilgi yükümlülüğü) ve KVKK Madde 10 (aydınlatma yükümlülüğü) gerekliliklerini karşılar. Presets kullanmadan da her site kendi hizmet bilgilerini `SERVICE_METADATA` yapısına ekleyebilir.
253
+
254
+ ---
255
+
256
+ ## Renk Temaları
257
+
258
+ 3 hazır tema, `data-blakfy-theme` ile seçilir:
259
+
260
+ | Tema | Açıklama | Attribute değeri |
261
+ | ---------------------- | ------------------------------------------ | ------------------------ |
262
+ | **Beyaz** (varsayılan) | Beyaz arka plan, koyu metin | `light` veya atlanabilir |
263
+ | **Açık gri** | `#f0f0f0` arka plan, koyu metin | `gray` |
264
+ | **Siyah** | `#1a1a1a` arka plan, açık metin | `dark` |
265
+ | **Otomatik** | Sistem `prefers-color-scheme` ayarını okur | `auto` |
266
+
267
+ ```html
268
+ <!-- Açık gri tema -->
269
+ <script src="...cookie.min.js" data-blakfy-theme="gray" ...></script>
270
+
271
+ <!-- Siyah tema -->
272
+ <script src="...cookie.min.js" data-blakfy-theme="dark" ...></script>
273
+
274
+ <!-- Sistem temasını takip et -->
275
+ <script src="...cookie.min.js" data-blakfy-theme="auto" ...></script>
276
+ ```
277
+
278
+ Tüm temalarda `--blakfy-accent` CSS değişkeni geçerliliğini korur — buton ve aktif sekme rengi `data-blakfy-accent` ile özelleştirilebilir.
279
+
280
+ ---
281
+
282
+ ## Configuration Reference
283
+
284
+ Tüm `<script>` tag'i üzerine konabilen `data-blakfy-*` attribute'ları:
285
+
286
+ | Attribute | Default | Tip | Açıklama |
287
+ | ---------------------------- | --------------------------------------------------- | ------------------ | ----------------------------------------------------------------------------------------------------- |
288
+ | `data-blakfy-locale` | `auto` | BCP47 ya da `auto` | Dil. `auto` → tarayıcıdan tespit. 23 desteklenen dil aşağıda. |
289
+ | `data-blakfy-main-lang` | `null` | BCP47 | Sitenin **birincil dili** (audit log için). Boşsa locale ile aynı. |
290
+ | `data-blakfy-policy-url` | `/cerez-politikasi` | URL | "Çerez Politikası" linki için hedef. |
291
+ | `data-blakfy-version` | `1.0` | string | **Politika versiyonu**. Bunu artırırsan tüm kullanıcılar tekrar onay vermek zorunda (re-consent). |
292
+ | `data-blakfy-audit-endpoint` | `null` | URL | Consent kararı bu endpoint'e POST edilir (KVKK Md.12 / GDPR Art.7(1) kanıt). |
293
+ | `data-blakfy-position` | `bottom-center` | enum | `bottom-center` \| `bottom-right` \| `bottom-left` \| `top-center` \| `top-right` \| `top-left` |
294
+ | `data-blakfy-theme` | `auto` | enum | **`light`** (beyaz) \| **`gray`** (açık gri) \| **`dark`** (siyah) \| `auto` (`prefers-color-scheme`) |
295
+ | `data-blakfy-accent` | `#3E5C3A` | hex | Buton ve vurgu rengi. |
296
+ | `data-blakfy-presets` | `null` | virgül listesi | Etkinleştirilecek preset key'leri. Örn: `ga4,gtm,facebook,clarity`. Aşağıda 18 preset listesi. |
297
+ | `data-blakfy-tcf` | `false` | bool | IAB TCF v2.2 modülünü aç (`__tcfapi` global). |
298
+ | `data-blakfy-cmp-id` | `0` | int | TCF CMP ID. `0` = preview/test mode. Sertifikasyon sonrası gerçek ID. |
299
+ | `data-blakfy-ccpa` | `auto` | enum | `auto` (jurisdiction tespiti), `true` (her zaman aç), `false` (kapat) |
300
+ | `data-blakfy-gpc` | `respect` | enum | `respect` (tarayıcı GPC'si auto-deny say) \| `ignore` |
301
+ | `data-blakfy-dnt` | `respect` | enum | `respect` (UI'da uyar) \| `auto-deny` (auto-reject) |
302
+ | `data-blakfy-status-url` | `cdn.jsdelivr.net/npm/@blakfy/cookie@2/status.json` | URL | Status bar mesajları için kaynak. Kendi domain'inde host'lamak için override et. |
303
+ | `data-blakfy-status` | `true` | bool | `false` ise status bar render edilmez. |
304
+
305
+ **Desteklenen 23 dil:** `tr`, `en`, `ar`, `fa`, `ur`, `fr`, `ru`, `de`, `he`, `uk`, `es`, `it`, `pt`, `nl`, `pl`, `sv`, `cs`, `zh`, `zh-TW`, `ja`, `ko`, `id`, `hi` (RTL: `ar`, `fa`, `ur`, `he`).
306
+
307
+ ---
308
+
309
+ ## API Reference
310
+
311
+ `window.BlakfyCookie` global olarak erişilebilir. Tüm v1 metodları korunmuştur, v2'de yeni metodlar eklendi.
312
+
313
+ | Metod | Sürüm | İmza | Açıklama |
314
+ | ----------------------- | ------ | -------------------------------------------------- | ------------------------------------------------------------------------------------------ |
315
+ | `version` | v1 | `string` | Kütüphane sürümü, örn. `"2.2.0"`. |
316
+ | `open()` | v1 | `() => void` | Tercihler modalını aç. |
317
+ | `acceptAll()` | v1 | `() => void` | Tüm kategorileri kabul et. |
318
+ | `rejectAll()` | v1 | `() => void` | Tüm kategorileri reddet (essential dışında). |
319
+ | `getConsent(cat)` | v1 | `(c: ConsentCategory) => boolean` | Kategori onaylı mı. `essential` her zaman `true`. |
320
+ | `getState()` | v1 | `() => BlakfyConsentState \| null` | Tam consent state objesi. |
321
+ | `onChange(fn)` | v1 | `(fn: (state) => void) => void` | State değişince çağrılır. |
322
+ | `setLocale(loc)` | v1 | `(loc: BlakfyLocale) => void` | Dili değiştir (UI re-render). |
323
+ | `getMainLang()` | v1 | `() => BlakfyLocale` | Audit log için ayarlanan birincil dil. |
324
+ | `onConsent(cat, fn)` | **v2** | `(c, fn: (granted: boolean) => void) => void` | Kategori-bazlı listener. Mevcut state ile **anında** çağrılır, sonra her değişimde tekrar. |
325
+ | `registerCleanup(opts)` | **v2** | `(opts: { category, cookies?, storage? }) => void` | Onay geri çekildiğinde silinecek cookie'leri/localStorage anahtarlarını kaydet. |
326
+ | `unblock(cat)` | **v2** | `(c: ConsentCategory) => void` | Kategori için tag-gating'i manuel aç. |
327
+ | `scan()` | **v2** | `() => ConsentCategory[]` | DOM'u tekrar tara (SPA navigasyonu sonrası). |
328
+ | `usePreset(name)` | **v2** | `(name: string) => Preset \| null` | Hazır preset'i runtime'da uygula. |
329
+ | `tcf.getTCString()` | **v2** | `() => string` | IAB TCF v2.2 TC string. |
330
+ | `ccpa.optOut()` | **v2** | `() => void` | CCPA "Do Not Sell" opt-out. |
331
+ | `ccpa.isOptedOut()` | **v2** | `() => boolean` | CCPA opt-out durumu. |
332
+ | `getJurisdiction()` | **v2** | `() => "GDPR" \| "CCPA" \| "LGPD" \| "default"` | Tespit edilen yetki alanı. |
333
+ | `window.__tcfapi(...)` | **v2** | IAB standart | `getTCData`, `addEventListener`, `removeEventListener` (TCF v2.2 spec). |
334
+
335
+ **ConsentCategory:** `"essential"` | `"analytics"` | `"marketing"` | `"functional"` | `"recording"`
336
+
337
+ **BlakfyConsentState alanları:** `id` (uuid), `essential` (true), `analytics`, `marketing`, `functional`, `recording`, `timestamp` (ISO), `version` (policy), `locale`, `mainLang`, `jurisdiction`, `tcString`, `uspString`.
338
+
339
+ ---
340
+
341
+ ## Tag-Gating
342
+
343
+ Üçüncü parti tag'lerini kullanıcı onay vermeden çalıştırma. 4 yöntem.
344
+
345
+ ### Harici script
346
+
347
+ ```html
348
+ <!-- type="text/plain" → tarayıcı çalıştırmaz -->
349
+ <script
350
+ type="text/plain"
351
+ data-blakfy-category="marketing"
352
+ data-blakfy-src="https://connect.facebook.net/en_US/fbevents.js"
353
+ async
354
+ ></script>
355
+ ```
356
+
357
+ Onay verildiğinde widget script'in `type` attribute'unu `text/javascript`'e çevirir, `data-blakfy-src` → `src` taşır ve DOM'a yeniden ekler.
358
+
359
+ ### Inline script
360
+
361
+ ```html
362
+ <script type="text/plain" data-blakfy-category="analytics">
363
+ console.log("Analytics tag fired");
364
+ fbq('init', 'PIXEL_ID');
365
+ </script>
366
+ ```
367
+
368
+ ### Iframe (YouTube, Vimeo, harita, vb.)
369
+
370
+ ```html
371
+ <iframe
372
+ data-blakfy-src="https://www.youtube.com/embed/VIDEO_ID"
373
+ data-blakfy-category="marketing"
374
+ data-blakfy-placeholder="auto"
375
+ width="560"
376
+ height="315"
377
+ title="YouTube video"
378
+ allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope"
379
+ allowfullscreen
380
+ ></iframe>
381
+ ```
382
+
383
+ `data-blakfy-placeholder="auto"` → otomatik "Çerez onayı bekleniyor" placeholder UI'ı gösterilir.
384
+
385
+ ### React/Next.js declarative gating
386
+
387
+ ```tsx
388
+ "use client";
389
+ import { useGating } from "@blakfy/cookie-next";
390
+
391
+ export function YouTubeEmbed({ id }: { id: string }) {
392
+ const allowed = useGating("marketing");
393
+ if (!allowed) {
394
+ return (
395
+ <div className="aspect-video rounded bg-zinc-100 flex items-center justify-center">
396
+ Onay bekleniyor bu içeriği görmek için pazarlama çerezlerini kabul edin.
397
+ </div>
398
+ );
399
+ }
400
+ return (
401
+ <iframe
402
+ src={`https://www.youtube.com/embed/${id}`}
403
+ className="w-full aspect-video"
404
+ title="YouTube"
405
+ allow="accelerometer; clipboard-write; encrypted-media; gyroscope"
406
+ allowFullScreen
407
+ />
408
+ );
409
+ }
410
+ ```
411
+
412
+ ---
413
+
414
+ ## 18 Hazır Preset
415
+
416
+ `data-blakfy-presets="ga4,gtm,facebook,clarity"` gibi virgüllü liste ile aktive edilir. Preset; cookie'leri ve localStorage anahtarlarını otomatik **registerCleanup**'a kaydeder, böylece kullanıcı onayı geri çekince bunlar otomatik silinir.
417
+
418
+ | Key | Araç | Kategori | Örnek Cookie / Storage |
419
+ | ----------- | ------------------ | --------------------- | ------------------------------------------------ |
420
+ | `ga4` | Google Analytics 4 | analytics | `_ga`, `_ga_*`, `_gid`, `_gat` |
421
+ | `gtm` | Google Tag Manager | analytics | `_dc_gtm_*` |
422
+ | `maps` | Google Maps | functional | `NID`, `SID`, `HSID` |
423
+ | `recaptcha` | Google reCAPTCHA | functional | `_GRECAPTCHA` |
424
+ | `facebook` | Facebook Pixel | marketing | `_fbp`, `_fbc`, `fr` |
425
+ | `youtube` | YouTube embed | marketing | `VISITOR_INFO1_LIVE`, `YSC`, `PREF` |
426
+ | `vimeo` | Vimeo embed | marketing | `vuid`, `_ga` |
427
+ | `hotjar` | Hotjar | analytics | `_hjSession*`, `_hjid` |
428
+ | `clarity` | Microsoft Clarity | analytics | `_clck`, `_clsk`, `MUID` |
429
+ | `linkedin` | LinkedIn Insight | marketing | `li_sugr`, `bcookie`, `lidc`, `UserMatchHistory` |
430
+ | `yandex` | Yandex Metrica | analytics + recording | `_ym_*`, `yandexuid`, `yabs-frequency` |
431
+ | `bing` | Bing Ads (UET) | marketing | `MUID`, `_uetsid`, `_uetvid` |
432
+ | `tiktok` | TikTok Pixel | marketing | `tt_*`, `_ttp` |
433
+ | `pinterest` | Pinterest Tag | marketing | `_pinterest_*`, `_pin_unauth` |
434
+ | `tawkto` | Tawk.to chat | functional | `__tawkUUID`, `Tawk_*` |
435
+ | `intercom` | Intercom | functional | `intercom-*` |
436
+ | `hubspot` | HubSpot | marketing | `__hssc`, `__hssrc`, `__hstc`, `hubspotutk` |
437
+ | `mailchimp` | Mailchimp | marketing | `_mcid`, `mc_*` |
438
+
439
+ **Manuel ekleme** (preset'sız):
440
+
441
+ ```js
442
+ window.BlakfyCookie.registerCleanup({
443
+ category: "marketing",
444
+ cookies: ["my_custom_pixel", /^_track_/],
445
+ storage: ["myAppMarketingCache"],
446
+ });
447
+ ```
448
+
449
+ ---
450
+
451
+ ## Compliance
452
+
453
+ | Yasa / Standart | Yetki Alanı | Modül | Ne yapar |
454
+ | -------------------------- | ------------------- | ------------------------------ | ------------------------------------------------------------------------ |
455
+ | **GDPR** | AB | `core/` + `ui/` | Eşit prominence kabul/red butonu, pre-tick yok, granular kategori onayı. |
456
+ | **ePrivacy Directive** | AB | `core/consent-store` | Onay öncesi cookie yazılmaz. |
457
+ | **KVKK** | Türkiye | `core/audit.js` | Md.12 ispat yükümlülüğü için audit log POST. |
458
+ | **CCPA / CPRA** | Kaliforniya | `compliance/ccpa.js` | "Do Not Sell" linki, USP string `1YYY`, GPC saygısı. |
459
+ | **LGPD** | Brezilya | `i18n/pt.js` + `geo/` | Portekizce UI, jurisdiction tespiti (v2.1'de tam destek). |
460
+ | **Google Consent Mode v2** | Google ekosistemi | `compliance/google-cmv2.js` | `gtag('consent','update', {...})` 7 sinyal. |
461
+ | **Microsoft UET** | Bing Ads + Clarity | `compliance/microsoft-uet.js` | `uetq.push('consent','update', {ad_storage})` |
462
+ | **Yandex Metrica** | Yandex ekosistemi | `compliance/yandex-metrica.js` | Tag-gating + Webvisor ayrı `recording` kategori. |
463
+ | **IAB TCF v2.2** | AB AdTech (AdSense) | `compliance/tcf-v2.js` | `__tcfapi`, TC string, GVL fetch. |
464
+ | **GPC** | Tarayıcı standardı | `compliance/gpc.js` | `navigator.globalPrivacyControl` → marketing/analytics auto-deny. |
465
+ | **DNT** | Tarayıcı standardı | `compliance/dnt.js` | `navigator.doNotTrack === "1"` → UI uyarı veya auto-deny. |
466
+
467
+ ### Kapsam dışı (Out of Scope)
468
+
469
+ Bu widget **çerez izni / tracking consent** için tasarlanmıştır. Aşağıdaki düzenlemeler **konu dışı** ve eklenti bunlara katkı sağlamaz:
470
+
471
+ | Düzenleme | Neden kapsam dışı |
472
+ | ----------------------------- | ---------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
473
+ | **HIPAA** (ABD - sağlık) | PHI (Protected Health Information) korunması; BAA imza, encryption, audit access log gerektirir. Hosting/uygulama katmanı sorumluluğudur (Wix Enterprise BAA, AWS BAA, Azure BAA). |
474
+ | **FERPA** (ABD - eğitim) | Öğrenci kayıt verisi; kurumsal access control + storage encryption sorunu. |
475
+ | **GLBA / SOX** (ABD - finans) | Finansal kayıt güvenliği; sistem-seviye audit ve encryption. |
476
+ | **PCI-DSS** | Ödeme kartı verisi; payment processor (Stripe/Adyen) sorumluluğu. |
477
+ | **SOC 2 / ISO 27001** | Organizasyonel security management; sertifikasyon süreçleri. |
478
+
479
+ **Bir HIPAA / PCI / FERPA-altında çalışan site bu widget'ı kullanabilir** — site'in çerez consent yükümlülüğü için tasarlandığı için sorun yaratmaz. Ancak widget'ın varlığı siteni o standartlara uyumlu yapmaz; o uyumluluklar **veri işleme + altyapı katmanlarında** çözülür.
480
+
481
+ ### TCF v2.2 (AdSense / Ad Manager kullanıcıları için)
482
+
483
+ `data-blakfy-tcf="true" data-blakfy-cmp-id="0"` ile preview mode'da çalışır. Production için IAB Europe sertifikasyonu gerekir (~2-3 ay, yıllık ~€2.000 ücret). Süreç ve audit gereksinimleri: [`docs/tcf-certification.md`](./docs/tcf-certification.md).
484
+
485
+ ```js
486
+ // TC string okuma
487
+ window.BlakfyCookie.tcf.getTCString();
488
+ // IAB standart API
489
+ window.__tcfapi("getTCData", 2, (data, success) => console.log(data));
490
+ ```
491
+
492
+ ### CCPA / CPRA (ABD trafiği)
493
+
494
+ `data-blakfy-ccpa="auto"` → `geo/jurisdiction.js` Kaliforniya tespit ederse:
495
+
496
+ - Banner'daki "Reddet" → **"Do Not Sell or Share My Personal Information"**
497
+ - Footer'a kalıcı `<a class="blakfy-ccpa-link">` (yasal zorunluluk)
498
+ - USP string `1YYY` (versiyon, opt-out, sale, third-party) `__uspapi` üzerinden expose edilir
499
+ - `Sec-GPC: 1` header otomatik opt-out sayılır
500
+
501
+ ### GPC / DNT default davranışı
502
+
503
+ - **GPC** (`navigator.globalPrivacyControl === true`): Kullanıcı açık onay vermediyse `marketing` ve `analytics` otomatik **denied**. CCPA jurisdiction'da yasal opt-out.
504
+ - **DNT** (`navigator.doNotTrack === "1"`): Default'ta sadece banner'da uyarı yazısı. `data-blakfy-dnt="auto-deny"` ile auto-reject yapılabilir. (DNT zayıf bir standart; GPC tercih edilir.)
505
+
506
+ ---
507
+
508
+ ## Troubleshooting
509
+
510
+ ### Widget hiç görünmüyor
511
+
512
+ 1. Console'da hata var mı? Network sekmesinde `cookie.min.js` 200 dönüyor mu?
513
+ 2. Script tag'i `</body>`'den önce mi? `<head>` içine konursa DOM hazır olmadan render denenir.
514
+ 3. Daha önce kabul/red verildiyse banner görünmez (kalıcı). Modal'ı tetiklemek için: `window.BlakfyCookie.open()` veya footer'a "Çerez Tercihleri" linki koy.
515
+
516
+ ### Banner her sayfada / her seferinde tekrar açılıyor
517
+
518
+ `data-blakfy-version` (politika versiyonu) değişti mi? Bu değer her cookie'de saklanır; uyuşmuyorsa re-consent tetiklenir. **Dikkat:** Kütüphane sürümü `cookie.min.js` patch güncellemeleri kullanıcıyı **etkilemez** (v2 düzeltmesi). v1'de bu bir bug'dı.
519
+
520
+ ### GTM / GA4 tetiklenmiyor
521
+
522
+ - **Bootstrap script GTM'den önce mi yükleniyor?** `cookie-defaults.min.js` `<head>`'in **ilk** script'i olmalı. Aksi halde GTM `consent default` çağrısını kaçırır.
523
+ - Tag → "Consent settings" → "Require additional consent for tag firing" doğru kategorilere ayarlı mı?
524
+ - Kullanıcı kabul etti mi: `window.BlakfyCookie.getState()` ile kontrol et.
525
+
526
+ ### Yandex Metrica çalışıyor ama Webvisor (session replay) çalışmıyor
527
+
528
+ Webvisor `marketing` değil, **`recording`** kategorisi gerektirir. Modal'da bu seçeneğe ayrı onay vermelisin. KVKK/GDPR yorumu: session replay biyometriye yakın özel veri olduğu için ek granular onay zorunlu.
529
+
530
+ ### TCF preview mode'da takıldım
531
+
532
+ `data-blakfy-cmp-id="0"` preview mode'dur. AdSense/Ad Manager production için IAB Europe sertifikasyonu sonrası atanan gerçek CMP ID girilmeli. Detay: [`docs/tcf-certification.md`](./docs/tcf-certification.md).
533
+
534
+ ### "Powered by Blakfy Studio" badge'i nasıl gizlerim?
535
+
536
+ **Gizlenemez.** 3 katmanlı anti-tampering korumalı (CSS `!important`, MutationObserver re-injection, kod-baked HTML). Lisans şartının parçasıdır. Marka koruması.
537
+
538
+ ### Next.js'te FOUC (içerik atlaması) görüyorum
539
+
540
+ `@blakfy/cookie-next@2`'ye yükselt. v1'de FOUC vardı; v2'de `next/script` `beforeInteractive` strateji kullanılıyor.
541
+
542
+ ### `acceptAll()` sonrası 3rd-party tag'ler hala engelli
543
+
544
+ `window.BlakfyCookie.scan()` çağır — SPA navigasyonu veya dinamik DOM ekleme sonrası gating observer'ı tekrar tetikler.
545
+
546
+ ---
547
+
548
+ ## Migration v1 → v2
549
+
550
+ **Kısa cevap:** Değişiklik gerekmez. v1 kullanıcıları `@1` etiketinde kalır, çalışmaya devam eder. v2'ye opt-in yapmak için **CDN URL'sini npm registry kaynağına çevir**:
551
+
552
+ ```diff
553
+ - <script src="https://cdn.jsdelivr.net/gh/tariktunc/blakfy-cookie@1/cookie.js"></script>
554
+ + <script src="https://cdn.jsdelivr.net/npm/@blakfy/cookie@2/dist/cookie.min.js"></script>
555
+ ```
556
+
557
+ Tüm v1 attribute'ları korunur, kullanıcı consent cookie'si geriye uyumlu okunur. v2'de yeni eklenen özellikler (3-tab modal, 3 renk teması, 18 preset, TCF v2.2, CCPA, GPC) **opt-in** — varsayılanlar v1 davranışını taklit eder.
558
+
559
+ Bundler kullanıyorsan: `npm i @blakfy/cookie` ve `import "@blakfy/cookie"`.
560
+
561
+ Detay: [`docs/migration.md`](./docs/migration.md).
562
+
563
+ ---
564
+
565
+ ## Footer "Çerez Tercihleri" linki
566
+
567
+ ```html
568
+ <a href="#" onclick="event.preventDefault(); window.BlakfyCookie.open();"> Çerez Tercihleri </a>
569
+ ```
570
+
571
+ Veya React:
572
+
573
+ ```tsx
574
+ <button onClick={() => window.BlakfyCookie?.open()}>Çerez Tercihleri</button>
575
+ ```
576
+
577
+ ---
578
+
579
+ ## Diğer Kaynaklar
580
+
581
+ - [`docs/architecture.md`](./docs/architecture.md) — modül yapısı, veri akışı, boyut bütçesi
582
+ - [`docs/compliance.md`](./docs/compliance.md) — her yasa için detaylı uyumluluk mappingleri
583
+ - [`CHANGELOG.md`](./CHANGELOG.md) — sürüm notları
584
+ - [`docs/migration.md`](./docs/migration.md) — v1 → v2 geçiş
585
+ - [`docs/tcf-certification.md`](./docs/tcf-certification.md) — IAB Europe başvuru süreci
586
+ - [`packages/cookie-next/README.md`](./packages/cookie-next/README.md) — npm paketi rehberi
587
+
588
+ ---
589
+
590
+ ## License
591
+
592
+ MIT © Blakfy Studio. "Powered by Blakfy Studio" branding badge anti-tampering korumalı ve kaldırılamaz.
593
+
594
+ Issues: https://github.com/tariktunc/blakfy-cookie/issues