Config reference
AvacyUserConfig (Partial<UserConfigType> + alcune estensioni Tier 1) è la forma di tutte le sorgenti di config: il banner.json del webspace pubblicato dal SaaS (lo scarica il loader v3), il blocco <script type="application/json" id="avacy-config"> in pagina e l'argomento di init(config). Tutte le chiavi qui sotto sono opzionali; i default vengono dal core (DEFAULT_CONFIG).
Source of truth nel codice:
packages/core/src/providers/providers.types.ts(ConfigType,UiConfig,StoreConfig,TcfFrameworkConfig, …)packages/cmp-web/src/public-api.ts(AvacyUserConfigconcore.autoInit)packages/cmp-web/src/init.ts+packages/core/src/merge-config.ts(merge delle sorgenti in pagina)packages/core/src/providers/config.ts(merge conDEFAULT_CONFIGe conremoteConfigUrl/remoteConfigName)
type AvacyUserConfig = Partial<UserConfigType> & {
core?: Partial<UserConfigType['core']> & { autoInit?: boolean };
};
Precedenza tra le sorgenti
La config effettiva nasce da più sorgenti. Dalla più forte alla più debole:
- Locale, cioè quello che sta nella pagina o la gestisce. In ordine: l'argomento di
init(config), il flag di debug (loggers),window.avacyFilters(filters), il blocco#avacy-config. L'argomento diinit()conta solo al primo boot e solo se chiami tuinit()(import della libreria): il loader v3 lo chiama senza argomento, e unwindow.Avacy.init(config)successivo è un no-op. - Remota: il
banner.jsondel webspace, scaricato dal loader v3. Il loader ci scrive sopraassetPath, che punta agli asset della release: anche questo, quindi, cede a unassetPathscritto in pagina. - Base:
DEFAULT_CONFIGdel core.
Il merge è profondo sugli oggetti (a qualunque livello) e sostituisce gli array. Un #avacy-config con solo { "ui": { "behavior": { "showShield": false } } } cambia quel campo e tiene colori, font e il resto di ui di banner.json. Un array scritto in pagina (frameworks, addons, loggers, …) invece prende il posto di quello remoto per intero: chi lo scrive lo fa apposta.
Due eccezioni:
cmpVersionecore.autoLoadli impone il bundle, qualunque sorgente li scriva.- Gli add-on da scaricare li decide solo
banner.json. Unaddonsin pagina sostituisce la lista che il core avvia, quindi può spegnere un add-on del webspace o cambiarne le impostazioni. Non può accenderne uno che il SaaS non ha attivato: il loader non lo scarica.
Senza blocco inline e senza debug vale banner.json, con l'assetPath della release, sopra DEFAULT_CONFIG.
Due merge diversi
- Sorgenti in pagina (
banner.jsondal loader,#avacy-config, debug,avacyFilters, argomento diinit()): merge profondo dimergeConfigLayers, come descritto sopra. remoteConfigUrl/remoteConfigName(vedi sotto): il file lo scarica il core e lo fonde piatto sotto la config che ha ricevuto: ogni chiave di primo livello scritta in pagina sostituisce quella remota per intero. Fanno eccezionecore(fuso chiave per chiave) eui, fuso un namespace alla volta (behavior,colors,font,layout,branding): dentro ogni namespace vince la pagina campo per campo, maframeworks[0].tcfscritto in pagina, per esempio, rimpiazza tuttoframeworksremoto.
Identità e versionamento
| Chiave | Tipo | Default | Descrizione |
|---|---|---|---|
publisherCountryCode | string (ISO 3166-1 alpha-2) | 'IT' | Country code del publisher, usato dal TCF (publisherCC nel TC string) e dalla logica di applicabilità di alcune normative locali. |
policyVersion | number | 1 | Versione della policy interna. Cambiandola si invalida automaticamente il consenso salvato (forza un re-consent al prossimo boot). |
cmpVersion | string | versione del pacchetto | Versione del CMP scritta nei metadati del consenso. Il bundle la impone a ogni boot con la versione di @avacy/cmp-web (oggi '3.0.0', la stessa di window.Avacy.version): un valore scritto in config viene ignorato. Il '1.0.0' di DEFAULT_CONFIG vale solo per chi usa @avacy/core da solo. |
consentExpiryDays | number | 180 | Scadenza del consenso in giorni (parità con cookie_expires_in_days del banner legacy). Un consenso più vecchio di tanto viene considerato non valido (consent.valid: false), il banner riappare. Un valore non numerico o ≤ 0 ricade su 180 con un warning. Governa anche la durata del cookie; quando il banner ricompare e il limite dei browser a 400 giorni: Durata del consenso. |
Boot e auto-init
core?: {
autoInit?: boolean; // solo loader Stencil `dist` di cmp-web
autoLoad?: boolean; // core
addonsTimeoutMs?: number; // core
}
| Chiave | Tipo | Default | Descrizione |
|---|---|---|---|
core.autoInit | boolean | true | Letta solo dal loader Stencil dist di @avacy/cmp-web (il pacchetto dietro un bundler), e solo dal blocco #avacy-config: con false quel loader non avvia il CMP e bisogna chiamare init(). Il loader v3 e cmpweb-web.auto.esm.js la ignorano e avviano comunque; cmpweb-web.esm.js non avvia mai da solo. Vedi Lifecycle › init e il boot programmatico nel Getting Started. |
core.autoLoad | boolean | true | Flag del solo @avacy/core, per chi chiama initCore direttamente. Se true, al termine di Core.init viene invocato triggerLoad() (emissione OnLoad listener chain). Se false, triggerLoad deve essere chiamato esplicitamente. Il bundle cmp-web lo forza a false e chiama triggerLoad() da sé: un valore nel #avacy-config viene ignorato. |
core.addonsTimeoutMs | number | 1500 | Quanto init() aspetta, in tutto, gli add-on pre che il loader ha annunciato, prima di proseguire senza. 0 = non aspettare. Un valore negativo o non numerico ricade su 1500 con un warning. Un add-on che arriva dopo parte comunque appena si registra. |
Lingua
| Chiave | Tipo | Default | Descrizione |
|---|---|---|---|
language | LanguageCode (ISO 639-1) | 'en' | Lingua attiva al boot. Le label vengono lette dalla bundle prebundled, eventualmente sovrascritta da customLabels[lang] e/o dal remote labels file. |
autoLanguage | boolean | false | Quando true, cmp-web rileva la lingua da document.documentElement.lang al boot e si riallinea ai cambi via MutationObserver. Mirror del flag auto_language di avacy_banner. |
customLabels | Partial<Record<LanguageCode, Partial<LanguageLabels>>> | undefined | Override partiali delle label per lingua. Precedenza: customLabels[lang] > remote labels > prebundled[lang] > prebundled fallback. Chiavi sconosciute vengono mergiate ma loggate come warning. |
Lingue disponibili
en, it e de sono compilate nel bundle. Per qualunque altra lingua il core scarica {assetPath}/languages/{lang}.json e lo fonde sopra l'inglese (LanguageProvider.loadLanguageData); se il file non c'è, resta l'inglese con un warning.
La release spedisce 32 file in assets/languages/ (sorgente: packages/core/public/assets/languages/):
ar, bg, bs, ca, cs, cy, da, el, es, eu, fi, fr, gl, he, hr, hu, id, ja, ko, lt, lv, mk, mt, nl, no, pl, pt, ro, ru, sl, uk, zh.
Ognuno ha tutte le label del banner e del CPC (59 su 61) più i nomi delle finalità GCM. Mancano in tutti solo IFRAME_BLOCKED_TEXT e IFRAME_BLOCKED_BTN, i testi del segnaposto dell'add-on preemptive-block, che in quelle lingue escono in inglese.
Con il loader v3 assetPath punta già agli asset della release, quindi non c'è niente da configurare. In inclusione manuale serve un assetPath corretto per ogni lingua diversa da en/it/de.
Nota a margine (limite noto): il core non imposta dir="rtl" né adatta il layout per lingue right-to-left, quindi ar e he hanno testo tradotto ma layout LTR.
Placeholder nel copy
Nove placeholder {{nome}} (tollerata la spaziatura interna, {{ nome }}) che vengono interpolati in qualunque label di customLabels — non solo l'informativa breve (SHORT_NOTICE_TEXT) o il sottotitolo del CPC (PREFERENCES_TEXT), come poteva sembrare dal legacy.
| Placeholder | Risolve a | Rete | Disponibile da SaaS |
|---|---|---|---|
{{vendors_count}} | Numero di fornitori attivi. Stringa vuota se non ancora noto o zero — mai il numero 0 in una frase. | Sì | Sì — tab "Informativa breve" |
{{basis_names}} | Unione dei nomi di purpose, special purpose, feature e special feature dichiarati, nell'ordine di preferencesOrder. Frase di fallback (solo en/it/de) finché non risolto. | Sì | Sì — tab "Informativa breve" |
{{purposes_names}} | Solo i purpose, stesso ordine. | Sì | No |
{{special_purposes_names}} | Solo gli special purpose. | Sì | No |
{{features_names}} | Solo le feature. | Sì | No |
{{special_features_names}} | Solo le special feature. | Sì | No |
{{stack_names}} | Solo i nomi degli stack IAB configurati (vedi tcf.stacks). | Sì | No |
{{privacy_policy_url}} | Un'àncora HTML completa (non l'URL nudo), testo da LABEL_PRIVACY_POLICY, verso PRIVACY_POLICY_URL. | No | No |
{{cookie_policy_url}} | Come sopra, per COOKIE_POLICY_URL/LABEL_COOKIE_POLICY. | No | No |
Gli ultimi tre di conservazione ({{storage_technology}}, {{item_name}}, {{expires_in_days}}) sono documentati a parte, insieme a ui.behavior.showConsentRetentionSection (vedi "La durata di conservazione si espone in due modi indipendenti" più sotto).
Il costo di rete si paga solo se il placeholder è davvero nel copy — resolvePlaceholders() espone tutti e nove come getter, ma t() legge solo quelli che il testo contiene: scriverne uno in una label è una decisione di performance, non solo di testo. Sono due le richieste possibili, non nove: {{vendors_count}} ne triggera una dedicata (vendor-list); {{basis_names}} e le quattro varianti _names più {{stack_names}} condividono la stessa richiesta (file lingua della GVL) — usarne più di uno nello stesso copy non ne raddoppia il costo.
⚠️ {{basis_names}} non è la base giuridica: nome simile, significato diverso — è l'elenco dei purpose/feature dichiarati, non se sono trattati a consenso o legittimo interesse (quello è enableLegInt, altra chiave, altro capitolo).
{{stack_names}} si risolve vuoto se tcf.stacks è assente, che è il default: un copy che lo usa deve restare leggibile anche senza. I due placeholder di policy rendono già un'àncora <a> completa — non vanno avvolti in un <a> proprio nel copy, o si ottiene un link dentro un link.
Disponibile da SaaS: oggi solo {{vendors_count}} e {{basis_names}} sono noti alla dashboard (tab "Informativa breve", campo di testo libero) — gli altri sette sono nuovi in cmp-web, nessuna tab li menziona.
Storage del consenso
store: {
type: 'cookie' | 'local-storage' | (string & {});
consentKey: string;
}
| Chiave | Tipo | Default | Descrizione |
|---|---|---|---|
store.type | StoreType | 'cookie' | Implementazione di storage attiva. Built-in: 'cookie', 'local-storage'. Per custom-store registrati via registerStoreProvider — il valore è opaco al core (string & {} accetta qualunque chiave registrata). |
store.consentKey | string | 'avacy_consent' | Nome della chiave dello storage (cookie name o localStorage key). Tipicamente lasciato di default per consistenza con la dashboard SaaS. |
⚠️
store.typeè una scelta statica, decisa una volta al boot — nessun fallback automatico. Con'cookie'e una config con molti framework combinati (es. TCF + ACM), il consenso codificato può superare il limite pratico di un cookie (~4KB): la scrittura viene troncata/rifiutata silenziosamente dal browser, senza errore. Dettaglio in Internals — storage.
Add-on (addons)
addons: (string | { [name: string]: Record<string, unknown> })[];
| Chiave | Tipo | Default | Descrizione |
|---|---|---|---|
addons | AddonItem[] | [] | Gli add-on attivi nel webspace, stessa forma di frameworks: il nome da solo ('preemptive-block') oppure un oggetto a una chiave con le impostazioni ({ "consent-archive": { "url": "…" } }). Il core normalizza tutto alla forma a oggetto e deduplica per nome: conta la prima posizione, le impostazioni delle voci successive si fondono sopra. Voci malformate (null, array, oggetti con più chiavi) vengono scartate con un warning. |
Chi scarica gli add-on è il loader v3, e legge la lista solo da banner.json. Un addons scritto in pagina sostituisce la lista che il core avvia: può spegnere un add-on del webspace o cambiarne le impostazioni, non accenderne uno che il SaaS non ha attivato. Dettagli, impostazioni di ogni add-on e tempi di attesa (core.addonsTimeoutMs) in Add-on.
Archivio consensi
Non è più una chiave di primo livello: è l'add-on consent-archive, acceso con una voce di addons[]. L'endpoint è il suo settaggio url, senza default: una voce senza url fa fallire l'add-on al setup, e il banner parte lo stesso.
{ "addons": [{ "consent-archive": { "url": "{API_URL}/{tenant}/v4/webspaces/{uuid}/consents" } }] }
Nella dashboard SaaS non è un campo che il cliente scrive a mano: la voce la scrive il SaaS dal toggle "Archiviazione dei consensi", con l'URL del proprio backend. Cosa invia, quando e dove tiene l'id di correlazione: vedi Add-on › consent-archive.
Asset path e remote config
| Chiave | Tipo | Default | Descrizione |
|---|---|---|---|
assetPath | string | '/assets' | Base path (o URL CDN) degli asset della release: file lingua ({assetPath}/languages/{lang}.json), immagini (shield, watermark), e i file indicati per nome con remoteConfigName, custom.customVendorsConfigName, acm.acmProvidersConfigName. Non governa la GVL TCF, che ha un suo URL (tcf.gvlUrl). Un path relativo viene completato con l'origine della pagina. Il loader v3 lo imposta da sé sugli asset della release. |
remoteConfigName | string | — | Filename di una config remota sotto assetPath, scaricata dal core (non dal loader v3). Nessun default: se mancano sia remoteConfigName sia remoteConfigUrl, il core non scarica niente. Merge piatto sotto le sorgenti in pagina, vedi Due merge diversi. Se il download fallisce il boot prosegue senza, con un warning. |
remoteConfigUrl | string | — | URL assoluto della config remota, alternativo a remoteConfigName (ignora assetPath). Se sono presenti entrambi, remoteConfigUrl vince — stessa precedenza di customVendorsUrl/acmProvidersUrl sui rispettivi *ConfigName. |
remoteGVLName | string | — | Presente nel tipo ma non letto da nessuno: non deriva tcf.gvlUrl, non ha effetto. |
vendorsCount | number | derivato | Override del contatore vendor mostrato in alcune label ("X vendor partecipano…"). Se assente, viene calcolato a runtime dal framework provider attivo. |
Frameworks
frameworks: FrameworkItem[];
type FrameworkItem =
| FrameworkType // shorthand: 'tcf' | 'gcm' | 'custom' | 'acm'
| { tcf: TcfFrameworkConfig }
| { gcm: GcmFrameworkConfig }
| { custom: CustomFrameworkConfig }
| { acm: AcmFrameworkConfig };
Array di framework da attivare. Ogni entry può essere lo shorthand 'tcf' (usa i default del framework) oppure la forma a oggetto con la config full per quel framework. Default: [] (nessun framework attivo — boot minimal, raro nella pratica).
TCF (frameworks: [{ tcf: { … } }])
| Chiave | Tipo | Default | Descrizione |
|---|---|---|---|
cmpId | number | 297 | CMP ID assegnato da IAB Europe. Per Avacy: 297. |
frameworkVersion | number | 2 | Versione TCF (2 = TCF v2). |
vendors | number[] | 'all' | 'all' | Lista vendor abilitati. 'all' = tutti i vendor del GVL attivo. |
gvlUrl | string | 'https://demo.avacy-cdn.com/assets/v3/' | Base URL della GVL (il GVL.baseUrl di @iabtechlabtcf/core). Non dipende da assetPath. |
purposeOneTreatment | boolean | false | Flag TCF purposeOneTreatment (pubCustomization): dichiara che il purpose 1 (storage info) è trattato dal publisher fuori dal TCF. Default false perché la normativa italiana richiede che l'utente esprima il consenso sul purpose 1. ⚠️ Governa solo il bit nella TC String: non nasconde il purpose 1 dal CPC (a differenza del banner legacy, dove la chiave faceva entrambe le cose). |
enableLegInt | 'all' | { purposes?, vendors? } | (off) | Opt-in per legittimo interesse. Se omesso, nessun purpose o vendor è legIntInteractable. 'all' = { purposes: 'all', vendors: 'all' }. ⚠️ Cambia la base giuridica scritta nella TC String, non solo la UI — vedi sotto. |
objectLegIntOnReject | boolean | true | Su RejectAll: se true, azzera anche hasLegitimateInterest. Se false, lascia LI al valore corrente e flippa solo hasConsent. |
enableAdvertiserConsentMode | boolean | true (se assente) | Flag di Google enableAdvertiserConsentMode in ogni risposta TCData di __tcfapi (anche getInAppTCData): i tag Google ricavano Consent Mode dalla TC String. Non dipende da GCM, come nel banner legacy. Con false il campo non viene scritto. Senza fornitori IAB (vendors: []) il flag resta sempre spento, qualunque sia il valore: le finalità 1, 3, 4 e 7 da cui Google ricava i segnali dipenderebbero solo dai fornitori custom. |
stacks | number[] | (assente) | Raggruppa purpose/special feature nel CPC secondo gli stack IAB del GVL. Non una lista di preferenze: deve essere un insieme disgiunto — vedi sotto. |
Restringere vendors nasconde automaticamente le finalità/funzionalità che nessun vendor superstite dichiara più. Non è una chiave nuova, è un effetto collaterale di vendors: purpose, special purpose, feature e special feature con zero vendor dichiaranti non compaiono come card a sé nel CPC — parità col banner legacy (PurposeContainerSnippet si comportava già così). Un integratore che testa con una lista di vendor ristretta e nota sparire alcune voci del secondo layer deve sapere che è voluto, non un bug del filtro.
⚠️ Eccezione: i fornitori custom contano anche loro. Se un fornitore custom dichiara un purpose TCF (CustomVendorConfig.purposes: [{framework: 'tcf', id}]) o una special feature (specialFeatures: [id], id IAB nudi), quella voce resta nel CPC anche se nessun vendor TCF superstite la dichiara, e il conteggio fornitori mostrato accanto somma le due fonti. Non è solo visibile: il toggle è attivabile, "accetta tutto" la accende e il consenso finisce nella TC String, come per una voce dichiarata da un vendor IAB. Special purpose e feature non hanno questa via: per loro il filtro dipende solo da vendors.
Base giuridica del legittimo interesse (enableLegInt)
enableLegInt non nasconde dei controlli nella UI: cambia la base giuridica che il CMP scrive per un vendor nella TC String. È la cosa più controintuitiva della superficie TCF, ed è il motivo per cui vale la pena leggerla prima di toccare la chiave in produzione.
Con la chiave assente (il default), il core scrive una Publisher Restriction di tipo REQUIRE_CONSENT su ogni purpose che un vendor dichiara sotto legittimo interesse e come flessibile (flexiblePurposes): quel purpose passa a consenso per quel vendor. Un purpose che il vendor dichiara sotto LI ma non flessibile diventa invece di fatto vietato per quel vendor — "should not process", nella terminologia della specifica.
Misurato sulla GVL in produzione: dei 358 vendor con almeno un purpose sotto legittimo interesse, 194 li hanno tutti flessibili, 128 nessuno, 36 in parte — 398 coppie (vendor, purpose) non commutabili semplicemente riaccendendo la UI. Con vendors: 'all', spegnere enableLegInt produce 6 Publisher Restriction su 677 coppie (vendor, purpose): sono poche, ma sono per-vendor e cambiano cosa un fornitore può fare, non solo cosa un utente vede.
Tre cose da sapere prima di usarla:
- Spegnerla è una scelta di base giuridica, non estetica: sposta l'onere legale su cui il vendor può processare quel dato, non solo un interruttore nel secondo layer.
- Accenderla per un sottoinsieme (
enableLegInt: { purposes: [...] }o{ vendors: [...] }) restringe solo le altre combinazioni — non è "tutto o niente". - Il legittimo interesse resta comunque vietato per i purpose 1, 3, 4, 5, 6 in TCF v2.2: accendere
enableLegIntnon li riporta sotto LI, la specifica lo esclude a prescindere dalla config.
Il dettaglio vendor nel CPC mostra la base effettiva: toccando la chiave, le stesse finalità possono cambiare sezione (consenso ↔ legittimo interesse) o sparire dall'elenco di un vendor. Esempio misurato, la stessa config che builds/web/assets/example.html spedisce — vendors: [29] (ETARGET SE, legIntPurposes: [2, 7, 10], flexiblePurposes: []): con LI spento al vendor resta una sola finalità su quattro (la 1, dichiarata a consenso); accendendo enableLegInt le altre tre tornano sotto legittimo interesse.
Stack IAB (stacks) — whitelist disgiunta, non una lista di preferenze
tcf.stacks raggruppa purpose e special feature nel secondo layer usando gli "stack" definiti dalla GVL — la stessa etichetta collettiva che IAB usa per accorpare finalità imparentate (es. "Pubblicità basata su dati limitati e misurazione delle prestazioni degli annunci"). Ma il TCF vieta di presentare lo stesso purpose in due gruppi diversi, e nella GVL reale gli stack si sovrappongono pesantemente: il purpose 2 sta in 28 stack su 45, e solo il 21% delle coppie di stack è disgiunto. Chi configura questa chiave a intuito, elencando gli stack che sembrano rilevanti, sbaglia circa 4 volte su 5.
Cosa succede quando la whitelist configurata non è disgiunta:
- L'ordine conta: il primo stack dichiarato che rivendica un purpose lo tiene.
- Uno stack sovrapposto viene scartato INTERO, non tagliato ai soli purpose non contesi. La ragione è che la descrizione IAB di uno stack non è modificabile: un gruppo dimezzato mostrerebbe un testo che nomina finalità che non contiene più.
- Il CMP scarta gli stack in eccesso con un warning in console (
[tcf] tcf.stacks: stack N is ignored because it overlaps…) — nessun errore bloccante, e nessun integratore lo leggerà se non lo sta cercando.
L'insieme disgiunto massimo già misurato sulla GVL è [1, 2, 10, 11, 21]: copre i purpose 2→10 e le due special feature, e lascia singoli solo il purpose 1 (non appartiene a nessuno stack) e l'11. È il punto di partenza da dare come configurazione, non solo un esempio.
Due vincoli aggiuntivi, non negoziabili via config:
- Nome e descrizione dello stack vengono dalla GVL e non sono sovrascrivibili da
customLabels— le TCF Policies lo vietano al publisher. Personalizzarli richiederebbe di alzare il flaguseNonStandardTextsnella TC String, oggi semprefalse. - La whitelist non tocca solo il CPC. Nel first layer, il nome dello stack sostituisce i nomi dei purpose che raggruppa dentro il placeholder
{{basis_names}}del copy — e l'ordine in cui{{basis_names}}li elenca segueui.behavior.preferencesOrder: chi riordina le sezioni del CPC riordina anche la frase del banner.
GCM (frameworks: [{ gcm: { … } }]) — Google Consent Mode v2
GCM si accende mettendolo in frameworks ('gcm' o { gcm: {…} }): non c'è un flag enabled. Da quel momento il SaaS scrive il flag gcm nel KeyValueStore della CDN, e la CDN serve la variante gcm del loader: questa chiama gtag('consent', 'default', …) con tutti e 7 i tipi a denied e wait_for_update: 500, in modo sincrono, prima dei tag Google. Il core chiama gtag('consent', 'update', …) quando legge un consenso salvato o ne scrive uno nuovo (crea dataLayer e gtag se la pagina non li ha). Con l'inclusione manuale del bundle il default va messo in pagina a mano. Gli stessi stati arrivano in avacy:consent-saved come detail.gcm.
I 7 purpose GCM, in ordine di id: 1 ad_storage, 2 ad_user_data, 3 ad_personalization, 4 analytics_storage, 5 functionality_storage, 6 personalization_storage, 7 security_storage.
| Chiave | Tipo | Default | Descrizione |
|---|---|---|---|
remoteGVLName | string | — | Presente nel tipo ma non letto da nessuno: nessun effetto. |
mapToPurpose | GcmMapToPurposeEntry[] | — | "Inferred mode": invece di esporre i 7 purpose GCM nel CPC, deriva il consenso GCM dagli altri framework. Ogni entry: { fw: 'tcf' | 'custom' | array, purpose: number | number[], gcmPurpose: number[] }. Con array su fw/purpose, tutte le sorgenti devono concedere (AND). |
Custom (frameworks: [{ custom: { … } }])
| Chiave | Tipo | Default | Descrizione |
|---|---|---|---|
customVendorsUrl | string | — | URL assoluto del JSON con vendor/purpose custom. Mutualmente esclusivo con customVendorsConfigName. |
customVendorsConfigName | string | — | Filename del config custom sotto assetPath. Risolto come assetPath + '/' + customVendorsConfigName. |
customVendorsConfig | CustomVendorsConfig (inline) | — | Config inline (evita la fetch HTTP). Utile per test e siti statici. |
customFetch | typeof fetch | window.fetch | Override della funzione fetch. Usato per stubbing nei test e per piattaforme con fetch non standard. |
enableLegInt | 'all' | { purposes?, vendors? } | (off) | Vedi tcf.enableLegInt, semantica identica. |
objectLegIntOnReject | boolean | true | Vedi tcf.objectLegIntOnReject. |
ACM (frameworks: [{ acm: { … } }]) — Google Additional Consent Mode
| Chiave | Tipo | Default | Descrizione |
|---|---|---|---|
acmProvidersUrl | string | — | URL assoluto della lista ATP (Google Additional Tech Provider). |
acmProvidersConfigName | string | — | Filename ATP sotto assetPath. |
acmProvidersConfig | AcmProvidersConfig (inline) | — | Config inline. |
acmFetch | typeof fetch | window.fetch | Override fetch. |
vendors | number[] | 'all' | 'all' | Sottoinsieme dei provider ATP da esporre. Con un array restano solo gli id elencati: consenso, CPC e stringa codificata si restringono a quelli. Speculare a tcf.vendors. |
UI (ui: UiConfig)
Comportamento UX di banner first-layer e CPC second-layer. Funzionalità/logica del consenso NON vivono qui — sono in frameworks o nelle scelte UX vincolate dal TCF.
Le chiavi di comportamento vivono sotto ui.behavior; ui.layout, ui.branding, ui.font e ui.colors sono documentati più sotto.
Banner e CPC applicano sempre la variante "compatta" quando rilevano window.self !== window.top: non è configurabile da ui.behavior. Un editor che incorpora il CMP nel proprio iframe e vuole mostrare il layout standard deve caricare la build cmp-saas-preview (packages/cmp-web/builds/cmp-saas-preview/), che disattiva la detection a livello di bundle, non di config.
| Chiave | Tipo | Default | Descrizione |
|---|---|---|---|
ui.behavior.showRejectAllButton | boolean | true nella build base, false esplicito per RAI | Bottone "Rifiuta tutto" nel footer del banner, paritetico ad "Accetta tutto" — stesso peso visivo, stesso handler di closeWithoutConsents (triggerConsent({type: 'RejectAll'})): i due controlli differiscono solo per posizione/peso, mai per semantica del consenso. Disponibile da SaaS: sì, la dashboard v3 scrive ui.behavior.showRejectAllButton. Vedi sotto per il vincolo con closeWithoutConsents. |
ui.behavior.closeWithoutConsents | boolean | 'text' | 'closeIcon' | false nello schema, ma 'closeIcon' nella build base (vedi vincolo sotto) | Azione "Continua senza accettare" nel banner. Funzionalmente equivalente a RejectAll. 'text' = pulsante outline con testo; 'closeIcon' = pulsante icon-only. Legacy true mappato a 'text'. Disponibile da SaaS: sì. |
ui.behavior.activateVendorsOnLegalScopeSelections | boolean | false | Se true, abilitare un purpose / special feature nel CPC abilita automaticamente i toggle dei vendor che lo supportano. |
ui.behavior.hideIntroOnScroll | boolean | false | Se true, il sottotitolo sotto l'heading del CPC collassa (opacity → 0, height → 0) dopo > 100px di scroll nel pannello. Mirror del legacy hide_intro_on_scroll. |
ui.behavior.closePreferencesOnBulkSelect | boolean | true | Se true, "Accetta tutto / Rifiuta tutto" nel preference center chiude il pannello dopo aver pubblicato il consenso. Se false, il pannello rimane aperto e si re-renderizza sul nuovo stato. |
ui.behavior.showPrivacyPolicyLink | boolean | false | Mostra il link alla Privacy Policy nel first-layer. Doppio gate: appare solo se true e se PRIVACY_POLICY_URL è impostata per la lingua attiva (vedi sotto) — un flag attivo senza URL non produce un link rotto, semplicemente non mostra nulla. |
ui.behavior.showCookiePolicyLink | boolean | false | Come sopra, per la Cookie Policy (COOKIE_POLICY_URL). Se entrambi i link sono visibili, un separatore li divide. |
ui.behavior.shieldPosition | 'left' | 'right' | 'right' | Lato dello schermo per lo shield persistente post-consenso (icona che riapre il CPC). |
ui.behavior.showShieldTooltip | boolean | false | Se true, il click sull'icona dello shield apre un tooltip con i link Privacy/Cookie Policy (stesso doppio gate di showPrivacyPolicyLink/showCookiePolicyLink) più un'azione per aprire il CPC, invece di aprire subito il CPC. Chiudibile con Esc. |
ui.behavior.closeWithoutSaving | boolean | false | Pulsante "chiudi senza salvare" nel footer del CPC: chiude il pannello senza pubblicare le modifiche fatte ai toggle. Vedi sotto per due comportamenti non ovvi. Disponibile da SaaS: sì, la dashboard v3 scrive ui.behavior.closeWithoutSaving. |
ui.behavior.showConsentRetentionSection | boolean | true nella build base | Aggiunge una voce di nav e una sezione "Conservazione" in coda al CPC, con tecnologia di storage, nome della chiave e durata massima. Non è lo stesso nome del legacy (show_cmp_cookie_retention_time) né della prima stesura di questo PBI (showCmpCookieRetentionTime, mai arrivata al codice) — vedi sotto per l'indipendenza dai placeholder {{storage_technology}}/{{item_name}}/{{expires_in_days}}. Disponibile da SaaS: sì, la dashboard v3 scrive ui.behavior.showConsentRetentionSection. |
ui.behavior.showShield | boolean | true | Shield persistente post-consenso (avacy-shield, l'icona che riapre il CPC). Con false non viene mai montato: utile quando il sito ha già un proprio link "Gestisci consenso" (showPreferenceCenter). Disponibile da SaaS: sì. |
ui.behavior.preferencesOrder | string[] | DEFAULT_PREFERENCES_ORDER | Ordine delle voci di nav (e delle sezioni) del CPC. Ogni voce è un id ${framework}::${tipo} (es. 'tcf::purpose', 'tcf::vendor', 'custom::vendor') oppure le voci extra 'glossary' e 'retention'. Id senza voce a runtime vengono saltati; voci non elencate restano in coda nel loro ordine naturale. Lo stesso ordine governa {{basis_names}} nel primo layer. |
showRejectAllButton / closeWithoutConsents — vincolo di coesistenza (PBI 19, 2026-08-19): un banner non mostra mai due modi testuali di rifiutare nello stesso primo layer. Quando showRejectAllButton è true, closeWithoutConsents non può valere 'text' — solo 'closeIcon' o false. Se lo si imposta comunque a 'text' (o al legacy true), il valore viene silenziosamente forzato a 'closeIcon', con un console.warn ([ConfigProvider] ui.behavior.showRejectAllButton is true: closeWithoutConsents cannot be 'text', forcing 'closeIcon'.) — non un errore bloccante, non visibile a chi non guarda la console (ConfigProvider.normalizeUiBehavior, packages/core/src/providers/config.ts).
Secondo pezzo, per chi confronta con la build precedente: nella build base il default di closeWithoutConsents è cambiato da assente/false a 'closeIcon', insieme all'accensione di showRejectAllButton. Chi non vedeva nessuna azione nell'header del banner nella build generica ora ne vede una. RAI non è impattata: spegne showRejectAllButton esplicitamente nella propria config (builds/rai/src/config.json).
ui.behavior.closeWithoutSaving — due cose che non si vedono leggendo solo il tipo:
- Il pulsante compare solo a consenso già dato (
avacy-cpc.tsx, condizionecloseWithoutSaving === true && isConsentGiven(...)). Chi accende la chiave e prova ad aprire il CPC alla prima visita — prima di aver mai dato un consenso — non lo vede, e può concludere che la chiave non funziona: non è un bug, è per costruzione. - Colore: 3 campi dedicati in
ui.colors—btnCloseWithoutSavingBackground,btnCloseWithoutSavingBackgroundHover,btnCloseWithoutSavingTextColor— stesso pattern degli altri ruoli pulsante (vedi Colori). Disponibile da SaaS: non come picker dedicato — i picker "Pulsanti" della dashboard v3 scrivono gli stessi valori su tutti i ruoli bottone, questo compreso. - Il testo del pulsante è la label
PREFERENCES_CLOSE_BTN, tradotta in tutte le lingue disponibili (vedi Lingue disponibili).
ui.secondLayerConsentOnGlobalSelection non è una chiave configurabile: non esiste nei tipi né ha un consumer nel core. Il comportamento è hardcoded sul true del legacy — "Accetta tutto / Rifiuta tutto" nel CPC salva sempre subito, senza "Salva e continua" — e il ramo false non è riproducibile. Chi arriva dal legacy con questa chiave assente (default legacy false) vedrà quindi un comportamento diverso da prima; solo RAI, che in produzione aveva già true esplicito, ha parità piena.
La durata di conservazione si espone in due modi indipendenti
Due meccanismi diversi mostrano la stessa informazione, e non sono collegati: accendere l'uno non spegne né sa dell'altro. Su un cliente che li usa entrambi, la durata compare due volte.
- I tre placeholder nel copy (
{{storage_technology}},{{item_name}},{{expires_in_days}}, vedi sopra "Placeholder nel copy") — esistono da sempre, guidati dal testo diPREFERENCES_TEXT, non da un flag. Oggi li usa solo RAI. ui.behavior.showConsentRetentionSection(sopra) — sezione dedicata in coda alla nav del CPC, indipendente dal copy.
Se un cliente ha già i tre placeholder nel proprio PREFERENCES_TEXT e la sezione è accesa (default true nella build base), l'utente vede la stessa durata due volte nello stesso pannello.
Le 4 label della sezione, e la quinta che manca apposta: PREFERENCES_RETENTION_HEADING (titolo nav + sezione), PREFERENCES_RETENTION_TEXT (intro, HTML), PREFERENCES_RETENTION_TECHNOLOGY e PREFERENCES_RETENTION_ITEM_NAME (etichette delle rispettive righe). La riga della durata non ha una label propria: riusa PREFERENCES_STORAGE_MAX_AGE, la stessa etichetta delle righe di max-age nella disclosure dei singoli vendor — cambiarla cambia due punti del CPC, non uno.
Il valore della durata passa da formatDuration (Intl), non dal copy: localizza e pluralizza da solo ("1 giorno" vs "180 giorni"), quindi non è sovrascrivibile. Le etichette della sezione sono tradotte in tutte le lingue disponibili. I due punti fra etichetta e valore li mette il CSS: metterli anche nel copy li raddoppia.
Il valore della tecnologia (cookie, local storage, SharedPreferences, UserDefaults, syndication) è quello che dichiara il provider di storage attivo, non tradotto — un cliente che ne vuole uno diverso a schermo non ha oggi una leva.
Righe senza valore non compaiono: se state.storage?.technology è vuoto, o consentExpiryDays non è impostata, quella riga non si vede — mai una riga con un lato vuoto.
Link Privacy/Cookie Policy — URL e testo per lingua
L'URL e il testo visibile dei link non vivono in ConfigType: sono per lingua, quindi vivono in customLabels, come qualunque altra label. Chiavi da impostare per ogni lingua supportata dal sito (un default mancante per una lingua ⇒ nessun link per quella lingua, anche a flag attivo):
| Chiave label | Descrizione |
|---|---|
PRIVACY_POLICY_URL | URL della propria Privacy Policy. Vuota di default — va impostata dal cliente. |
COOKIE_POLICY_URL | URL della propria Cookie Policy. Vuota di default. |
LABEL_PRIVACY_POLICY | Testo del link. Default "Privacy Policy" (en/it), "Datenschutzerklärung" (de) — override solo se si vuole un testo diverso. |
LABEL_COOKIE_POLICY | Testo del link. Default "Cookie Policy" (en/it), "Cookie-Richtlinie" (de). |
{
"ui": {
"behavior": {
"showPrivacyPolicyLink": true,
"showCookiePolicyLink": true
}
},
"customLabels": {
"it": {
"PRIVACY_POLICY_URL": "https://example.com/privacy",
"COOKIE_POLICY_URL": "https://example.com/cookie"
},
"en": {
"PRIVACY_POLICY_URL": "https://example.com/en/privacy",
"COOKIE_POLICY_URL": "https://example.com/en/cookie"
}
}
}
Layout (ui.layout)
| Chiave | Tipo | Default | Descrizione |
|---|---|---|---|
ui.layout.bannerPosition | 'bottom' | 'top' | 'center' | 'bottom' | Lato del viewport a cui è ancorato il banner. Disponibile da SaaS: sì. |
ui.layout.bannerFloating | boolean | true | true: card staccata dai bordi, con ombra e angoli arrotondati. false: edge-to-edge. Ignorata con bannerPosition: 'center', che è sempre floating. |
ui.layout.bannerAnimation | false | 'FadeIn' | 'SlideIn' | 'FadeIn' | Animazione d'ingresso. SlideIn scorre dal basso con bottom e dall'alto con top; con center ricade su FadeIn. prefers-reduced-motion vince sempre. Disponibile da SaaS: sì. |
Le combinazioni valide sono cinque: bottom/top × floating/edge, più center. Quando il CMP gira in un iframe usa il layout compatto e questi tre campi non hanno effetto.
Per chi migra dal legacy: PositionBottom/PositionTop/PositionCenter → bottom/top/center; banner_animation: "Floating" non era un'animazione ma un layout, e corrisponde a bannerFloating: true con bannerAnimation: false.
Branding (ui.branding)
| Chiave | Tipo | Default | Descrizione |
|---|---|---|---|
ui.branding.enableBranding | false | 'light' | 'dark' | assente (disattivato) | Watermark "Avacy" nel first-layer e nel CPC. Il gate è contrattuale, non tecnico: la chiave resta lato client come le altre. |
ui.branding.logoUrl | string | assente | Logo del cliente/tenant, mostrato accanto al titolo nel first-layer. Non più mostrato nel CPC (rimosso il 2026-07-31, ratificato il 2026-08-17). Fallback pulito (elemento rimosso) se l'immagine non carica. |
ui.branding.shieldIconUrl | string | assente | Icona custom del widget persistente post-consenso, al posto dell'icona di default Avacy. Vedi sotto per il contratto di ricolorazione e per chi può impostarla via SaaS. |
Icona custom del widget preferenze — ricolorazione
Un file SVG referenziato da shieldIconUrl viene ricolorato automaticamente se contiene, nei propri attributi fill, il placeholder:
fill="var(--avacy-shield-primary-bg, #fallback)"
Il valore risolve a ui.colors.shieldColor (via la custom property --avacy-shield-primary-bg), quindi l'icona segue il colore del tema del cliente senza dover caricare un file diverso per ogni brand. accentPrimary non esiste più in UiColorsConfig (eliminata nel PBI 08): la chiave corretta è shieldColor. Se il file non contiene il placeholder, o è un formato raster (PNG/JPG), viene mostrato così com'è, nessuna ricolorazione.
Requisito: il server che ospita il file SVG deve rispondere con header CORS permissivi (Access-Control-Allow-Origin) — a differenza di un normale <img src>, l'icona viene scaricata via fetch() per poter leggere e iniettare il suo contenuto. Se il fetch fallisce (rete, 404, CORS mancante), il widget mostra l'icona di default Avacy, senza rompersi.
Chi può impostarla via SaaS (policy di prodotto, non un limite del CMP): il componente tratta shieldIconUrl come una chiave generica, senza distinzioni per tenant. Nella dashboard SaaS però non è un campo self-service: oggi l'unico tenant che riceve un valore è EasyConsulting, iniettato lato backend (EasyConsultingOverrides.php). Il colore dello shield invece lo scrive la dashboard v3 per tutti: il picker "Link e interazioni" valorizza ui.colors.shieldColor (insieme a link, tab, checkbox e notice). Chi scrive config direttamente non ha restrizioni: è una policy del SaaS, non un vincolo del CMP.
{
"ui": {
"branding": {
"shieldIconUrl": "https://cdn.example.com/icons/my-shield.svg"
}
}
}
Distanziare lo scudo dai bordi della pagina
Lo scudo è ancorato di default a 1rem dal basso e dal lato su cui è posizionato (ui.behavior.shieldPosition). Se il sito ha altri elementi fissi che si sovrappongono (una chat, un'altra cookie bar, un bottone fisso), si scosta con due CSS custom property dal foglio di stile del sito — non sono una chiave di configurazione, non hanno un default in DEFAULT_CONFIG e non compaiono nello schema ConfigType:
:root {
--avacy-shield-offset-bottom: 4rem; /* distanza dal basso */
--avacy-shield-offset-side: 4rem; /* distanza dal lato su cui è posizionato */
}
| Variabile | Default | Descrizione |
|---|---|---|
--avacy-shield-offset-bottom | 1rem | Distanza dal bordo inferiore della pagina. |
--avacy-shield-offset-side | 1rem | Distanza dal bordo laterale su cui lo scudo è posizionato. Segue automaticamente il lato configurato (destra o sinistra) — non serve impostarne due. |
Accettano qualunque unità CSS valida per una lunghezza (px, rem, %, calc(), incluso env(safe-area-inset-bottom) per tenere conto dell'home indicator su iOS).
⚠️ Se si imposta un valore non valido come lunghezza (es. un numero senza unità), il browser non torna al default: la proprietà si comporta come se non fosse impostata affatto, e lo scudo può spostarsi in una posizione inattesa. Controllare sempre visivamente dopo aver personalizzato questi valori.
Disponibile da SaaS: non ancora — oggi si imposta solo dal CSS del sito. È prevista una gestione futura da dashboard (nota di Erica, 2026-09-01); finché non arriva, questa resta l'unica via.
Font (ui.font)
| Chiave | Tipo | Default | Descrizione |
|---|---|---|---|
ui.font.fontFamily | string | 'Arial, Helvetica, sans-serif' | Font-family per corpo, titoli e label — una sola chiave per tutte e tre le famiglie. Un valore vuoto o di soli spazi non emette nessuna custom property e resta il default del design system. Disponibile da SaaS: sì — la dashboard v3 offre una tendina di font web-safe più un campo per un font custom, e scrive ui.font.fontFamily. Il CMP non scarica il webfont: un font non di sistema deve caricarlo il sito (vedi sotto). |
Override dal CSS del sito — tre slot indipendenti
fontFamily non scrive direttamente il font-family finale: alimenta la custom property --avacy-font-family-config, che sta nella posizione di fallback di una catena var() a tre livelli. Il sito ospite può sovrascrivere la config, per singolo scope, con tre custom property proprie:
:root {
--avacy-global-font-family-custom: 'Inter', sans-serif; /* corpo — fallback anche per titoli e label */
--avacy-heading-font-family-custom: 'Playfair Display', serif; /* solo titoli */
--avacy-label-font-family-custom: 'Inter', sans-serif; /* solo label */
}
Chi imposta solo --avacy-global-font-family-custom cambia corpo, titoli e label insieme (titoli e label ricadono sul globale se non impostati singolarmente). Chi vuole differenziare i titoli imposta anche --avacy-heading-font-family-custom, e così per le label. Le tre custom property vincono sempre su fontFamily, indipendentemente dall'ordine di caricamento dei fogli di stile: attraversano il boundary dello shadow DOM per eredità, quindi si impostano su :root o su un qualunque antenato della pagina ospite — non serve toccare il bundle.
Nessun caricamento automatico del webfont: impostare una famiglia qui non inietta un <link> o una regola @font-face — a differenza del legacy (font_autoload), rimosso perché caricare un webfont da un provider esterno prima del consenso è un problema di sostanza per un CMP, non di forma. Il sito deve rendere disponibile il font da sé, in entrambi gli scenari seguenti.
Come impostare il proprio font
Font già disponibile sul sito (font di sistema, o un font che il sito carica già per conto proprio) — nessun caricamento aggiuntivo, si indica solo il nome:
{
"ui": {
"font": {
"fontFamily": "Arial, sans-serif"
}
}
}
Font da un provider remoto (es. Google Fonts) — il sito lo include a modo suo, poi lo referenzia nella config:
<!-- nella pagina ospite, prima o dopo il boot del CMP: indifferente -->
<link rel="preconnect" href="https://fonts.googleapis.com">
<link href="https://fonts.googleapis.com/css2?family=Inter:wght@400;600&display=swap" rel="stylesheet">
{
"ui": {
"font": {
"fontFamily": "Inter, sans-serif"
}
}
}
In entrambi i casi funziona identicamente anche via CSS con i tre slot --avacy-*-font-family-custom sopra: cambia solo dove il valore vive, non il meccanismo di caricamento. È lo stesso schema che usa RAI in produzione (rai-custom.css, tutte e tre le famiglie a Inter, dichiarata staticamente nel proprio CSS — nessuna delle due porta a un caricamento gestito dal CMP).
Colori (ui.colors)
64 campi, tutti opzionali: un campo assente prende il valore di DEFAULT_CONFIG.ui.colors, che li valorizza tutti (le custom property dei colori non hanno un fallback CSS proprio: a differenza del font, il valore arriva sempre dalla config). La dashboard v3 li scrive tutti, raggruppati in 10 picker che scrivono lo stesso valore su più campi. Fonte autorevole: COLOR_TOKEN_MAP in packages/cmp-web/src/helpers/theming.ts, che ogni campo qui sotto scrive su una custom property applicata inline su :root — vince sempre su qualunque stylesheet, incluso un CSS custom del cliente, indipendentemente dall'ordine di caricamento.
Tre trappole che un elenco piatto non racconta:
- I campi bottone sono nominati per ruolo business, non per stile. Non esistono
primary/secondary/outline/textcome nel legacy: ogni bottone del CMP ha un ruolo (accept,reject,customize, …) e un campo dedicato. Chi cerca "il colore del bottone primario" non trova niente — deve sapere quale bottone, non che aspetto ha. textColorPrimaryetextColorSecondarysono invertiti rispetto al nome. Eredità del legacy:textColorPrimaryè il testo secondario/muto (chiaro),textColorSecondaryè il testo principale (scuro). Vedi il box sotto.- I campi di palette globale sono condivisi da più componenti (tab, checkbox, link, notice, CPC, label): hanno un blast radius diverso dai colori bottone, che restano isolati al proprio ruolo.
Bottoni per ruolo
Ogni ruolo ha 3 campi (background, background hover, colore testo), tranne dove annotato. Nessun ruolo "tertiary" — quel concetto di stile è caduto insieme a primary/secondary/outline/text.
| Ruolo | Dove | Background | Background hover | Testo | Altro |
|---|---|---|---|---|---|
Accept (Accetta tutto) | banner | btnAcceptBackground | btnAcceptBackgroundHover | btnAcceptTextColor | — |
Reject (Rifiuta tutto, PBI 19) | banner | btnRejectBackground | btnRejectBackgroundHover | btnRejectTextColor | Default identico ad Accept — nessun colore "negativo" dedicato, decisione in attesa di review Design. |
Customize (Personalizza → apre CPC) | banner + shield | btnCustomizeBackground | btnCustomizeBackgroundHover | btnCustomizeTextColor | Stesso ruolo/colore in entrambi i punti, anche se rendono con forme diverse. |
CloseWithoutConsent ("Continua senza accettare") | banner | btnCloseWithoutConsentBackground | btnCloseWithoutConsentBackgroundHover | btnCloseWithoutConsentTextColor (+ btnCloseWithoutConsentTextColorHover) | + btnCloseWithoutConsentBorder (bordo, tratto distintivo di questo ruolo). L'hover si riempie (non si scurisce), per questo background/testo hover sono campi indipendenti, non incatenati al base. |
ActivateAll (Attiva tutto, CPC) | CPC | btnActivateAllBackground | btnActivateAllBackgroundHover | btnActivateAllTextColor | — |
DeactivateAll (Disattiva tutto, CPC) | CPC | btnDeactivateAllBackground | btnDeactivateAllBackgroundHover | btnDeactivateAllTextColor | — |
Save (Salva e continua, CPC) | CPC | btnSaveBackground | btnSaveBackgroundHover | btnSaveTextColor | — |
CloseWithoutSaving ("Chiudi" del footer CPC, PBI 14) | CPC | btnCloseWithoutSavingBackground | btnCloseWithoutSavingBackgroundHover | btnCloseWithoutSavingTextColor | Default identico a Save (i due stanno affiancati) — non confondere con CloseWithoutConsent, altro bottone, altro layer. |
Back (torna alla lista, CPC) | CPC | — (sempre trasparente) | — | btnBackTextColor (+ btnBackTextColorHover, cambia famiglia di colore, non solo intensità) | Non un ruolo di consenso, ma configurabile — RAI lo personalizza in produzione. |
icon-only (varianti icona sola, es. CloseWithoutConsent in forma icona) non ha campi colore propri.
Palette globale — condivisa da più componenti
| Chiave | Custom property | Descrizione |
|---|---|---|
textColorPrimary | --avacy-color-secondary-text | ⚠️ Nome invertito: nel design system è il testo secondario/muto (chiaro), non il principale. Eredità del ruolo legacy (text_color_primary era già il grigio chiaro lì). |
textColorSecondary | --avacy-color-text | ⚠️ Nome invertito: è il testo principale (scuro/quasi-nero), non il secondario. |
backgroundColor | --avacy-color-bg | Sfondo principale di banner e CPC. |
secondaryBackgroundColor | --avacy-color-secondary-bg | Sfondo secondario (es. bordo sotto il watermark). |
Non esiste più un concetto di "colore d'accento" condiviso (accentPrimary/accentSecondary, eliminati nel PBI 08): ogni elemento sotto ha la propria variabile granulare — vedi tab/checkbox/link/shield/notice qui sotto, che nel legacy riusavano tutti accent_primary.
Altri componenti
| Chiave | Custom property | Dove/cosa |
|---|---|---|
checkboxBorderColor | --avacy-checkbox-border-color | Bordo del checkbox non selezionato. |
checkboxBackground | --avacy-checkbox-bg | Sfondo del checkbox non selezionato. |
checkboxBackgroundChecked | --avacy-checkbox-bg-checked | Sfondo del checkbox selezionato. |
checkboxBorderChecked | --avacy-checkbox-border-checked | Bordo del checkbox selezionato — campo indipendente dal background. |
checkboxCheckColor | --avacy-checkbox-check-color | Colore della spunta dentro il checkbox selezionato. |
sliderBackground | --avacy-toggle-track-bg | Track del toggle, stato spento. |
sliderBackgroundChecked | --avacy-toggle-track-bg-checked | Track del toggle, stato acceso. |
sliderBackgroundCircle | --avacy-toggle-thumb-bg | Pallino/thumb del toggle. |
tabDesktopTextColor | --avacy-tab-desktop-color | Testo del tab desktop (sidebar) in hover/active — non confondere con tabTextColor. |
tabIndicatorColor | --avacy-tab-accent-color | Indicatore verticale del tab attivo (desktop). |
tabColor | --avacy-tab-color | Testo del tab desktop a riposo. |
tabIndicatorColorInactive | --avacy-tab-indicator-color-inactive | Indicatore verticale dei tab inattivi (desktop) — non confondere con tabIndicatorColor. |
tabBackground | --avacy-tab-fill-bg | Sfondo pieno del tab/pillola in hover/active, solo variante mobile. La variante desktop non riempie mai lo sfondo. |
tabTextColor | --avacy-tab-fill-color | Testo sopra tabBackground — stesso ambito, solo mobile. |
linkColor | --avacy-link-color | Link (CPC, glossario, shield). |
linkColorHover | --avacy-link-hover-color | Link in hover. |
cpcBackground | --avacy-cpc-bg | Sfondo del pannello CPC. |
cpcTextColor | --avacy-cpc-color | Testo principale del CPC. |
cpcTextSecondaryColor | --avacy-cpc-text-secondary | Testo secondario/muto del CPC. |
cpcTitleColor | --avacy-cpc-title-color | Titolo principale del CPC (es. "Privacy settings:"). Default #262628. |
cpcBorderColor | --avacy-cpc-border-color | Divisori tra le sezioni disclosure nella lista del CPC. |
stackMembersBackground | --avacy-cpc-stack-members-bg | Sfondo del box membri di uno stack IAB TCF (PBI 16) quando la card è espansa. |
shieldColor | --avacy-shield-primary-bg | Icona persistente post-consenso (shield), solo quando non è un'icona custom con proprio fallback — vedi Branding sopra. |
labelColor | --avacy-label-color | Label condivise (label del toggle, titolo accordion). |
noticeBackground | --avacy-notice-bg | Sfondo del componente notice (es. box di legittimo interesse). |
noticeColor | --avacy-notice-color | Testo e icona del notice (--avacy-notice-icon-color eredita in catena). |
errorTextColor | --avacy-color-error-text | Testo di errore (es. caricamento fallito nella lista disclosure). |
focusColor | --avacy-color-focus | Outline di :focus-visible, condiviso da tutti gli elementi interattivi, nessuna eccezione — un solo slot, non personalizzabile per singolo elemento. Default #282828. Il contrasto rispetto allo sfondo è responsabilità di chi sceglie il colore. |
overlayBackground | --avacy-overlay-bg | Overlay scuro dietro banner e CPC. Accetta qualunque sintassi CSS color, incluso rgba con alpha. |
scrollbarThumbColor | --avacy-scrollbar-thumb-bg | Pallino/thumb della scrollbar custom (CPC). |
scrollbarTrackColor | --avacy-scrollbar-track-bg | Binario della scrollbar custom (CPC). |
disclosureSubtitleColor | --avacy-disclosure-subtitle-color | Sottotitolo nei pannelli disclosure (CPC). |
Logger
| Chiave | Tipo | Default | Descrizione |
|---|---|---|---|
loggers | ('console' | 'event')[] | [] (silenzioso) | Quali logger attivare. 'console' stampa info/warning/error su DevTools; 'event' emette ogni riga come evento DOM avacy:log ({ level, message, topic }). Combinabili. window.Avacy.setDebug(true) sostituisce l'intero array con ['console'] al prossimo boot (persiste in localStorage). |
Filtri (advanced)
| Chiave | Tipo | Default | Descrizione |
|---|---|---|---|
filters | ContextFilters | — | Pipeline dichiarativa di filtri applicati prima di scrivere un pezzo dello stato del CMP e di notificarlo (a getData(), agli eventi, alla UI). Una funzione per chiave dello stato, sync only. Dalla pagina si passa con window.avacyFilters. |
consentFilter | (choices: PrivacyChoice[], meta: ChangeMeta) => PrivacyChoice[] | — | Riscrive le scelte di un consenso prima che vengano codificate e salvate, così l'adattamento sopravvive al ricaricamento della pagina (un filtro filters.consent cambia solo lo stato in memoria). Sync e puro. meta porta il tipo risolto (type), quello richiesto (requestedType), lo screen e le scelte in vigore prima del salvataggio (currentChoices). Uso avanzato, pensato per le build custom. |
Sono funzioni: non si possono scrivere in banner.json né nel blocco #avacy-config (JSON). filters arriva da window.avacyFilters o dall'argomento di init(config); consentFilter solo dall'argomento di init(config).
window.avacyFilters
Tier 1. L'unico modo per passare filtri dalla pagina quando il CMP lo avvia il loader v3 (o cmpweb-web.auto.esm.js): il blocco #avacy-config è JSON e non può contenere funzioni, e l'argomento di init() lo usa solo chi avvia il CMP da sé. init() legge window.avacyFilters e lo usa come valore di filters.
window.avacyFilters = {
[K in keyof ContextData]?: (
next: ContextData[K], // il valore che sta per essere scritto
prev: ContextData[K] | undefined, // quello attuale
meta: ChangeMeta, // da dove viene la modifica
) => ContextData[K]; // il valore da scrivere davvero
};
type ChangeMeta = {
type?: string; // per un consenso: 'AcceptAll' | 'RejectAll' | 'Personalized' (il tipo salvato)
screen?: 'first' | 'second' | 'api';
source?: string; // 'listener' per una scrittura di consenso
};
meta è valorizzato quando il visitatore (o l'API) scrive un consenso; per il consenso riletto dallo storage al caricamento, e per le altre chiavi, è un oggetto vuoto.
Le chiavi sono quelle di getData(): consent, config, language, customization, glossary, stacks, standardTexts, storage, core… Regole:
- Quando: va definito prima che
init()parta, quindi in uno script sincrono prima del tag del loader. Una modifica successiva vale dal prossimoreload(), che lo rilegge. - Precedenza: sopra
#avacy-confige sotto il flag di debug e l'argomento diinit(config)(vedi Precedenza). Il merge è per chiave: unfilters.consentpassato ainit(config)sostituisce solo quello diavacyFilters, gli altri restano. - Sync: la funzione deve restituire il valore, non una Promise.
- Errori: se il filtro lancia un'eccezione, il CMP scrive un warning nei log e tiene il valore originale. Se restituisce
null/undefined, vale il valore originale. - Solo in memoria: un filtro su
consentcambia lo stato che vedono la pagina, gli eventi e la UI, ma non il consenso salvato nello storage né la TC String. Per cambiare quello che si salva serveconsentFilter.
<script>
// Tiene sempre spento il fornitore custom d116 nello stato letto dalla pagina.
window.avacyFilters = {
consent(next) {
if (!next.ready || !next.valid) return next;
return {
...next,
choices: next.choices.map((c) =>
c.framework === 'custom' && c.type === 'vendor' && c.id === 'd116'
? { ...c, hasConsent: false }
: c,
),
};
},
};
</script>
<script src="https://<tenant>.avacy-cdn.com/v3/avacy-cmp-web.js?webspace=<uuid>"></script>
Esempio completo (TCF + custom + UI tweak)
{
"publisherCountryCode": "IT",
"language": "it",
"policyVersion": 2,
"consentExpiryDays": 365,
"store": { "type": "cookie", "consentKey": "avacy_consent" },
"loggers": ["console"],
"assetPath": "https://cdn.example.com/avacy",
"frameworks": [
{
"tcf": {
"cmpId": 297,
"frameworkVersion": 2,
"vendors": "all",
"purposeOneTreatment": false,
"enableLegInt": "all",
"objectLegIntOnReject": true
}
},
{
"custom": {
"customVendorsConfigName": "custom-vendors.json"
}
}
],
"ui": {
"behavior": {
"closeWithoutConsents": "text",
"closePreferencesOnBulkSelect": true
}
},
"customLabels": {
"it": {
"ACCEPT_ALL_BTN": "Acconsento"
}
}
}
See also
init— boot programmatico con override config- Tier 2 —
getConfig— snapshot read-only della config dopo il merge - Add-on —
addons[]e le impostazioni di ogni add-on