<button type="button" class="rtds-popover__trigger" popovertarget="popover-with-close" aria-expanded="false">
Dettagli
</button>
<div popover id="popover-with-close" class="rtds-popover__panel" data-preferred-placement="bottom" aria-labelledby="popover-with-close-title">
<div class="rtds-popover__arrow" aria-hidden="true"></div>
<div data-popover>
<div id="popover-with-close-title" class="rtds-popover__header">
<h3 class="rtds-popover__title">Titolo pannello</h3>
</div>
<div class="rtds-popover__body" tabindex="0">
<p>Contenuto ricco con pulsante di chiusura esplicito.</p>
</div>
<button type="button" class="rtds-btn rtds-btn--icon rtds-btn--outlined rtds-border-none rtds-content-01 hover:rtds-bg-black focus:rtds-content-03 rtds-w-8 rtds-h-8 rtds-popover__close">
<svg class="rtds-icon rtds-fill-current rtds-w-4 rtds-h-4" aria-hidden="true" focusable="false" role="img">
<use href="../../icons.svg#mini--x-mark" />
</svg>
<span class="rtds-sr-only">Chiudi</span>
</button>
</div>
</div>
{%- set _popoverId = popoverId | default('popover') -%}
<button type="button"
class="rtds-popover__trigger"
popovertarget="{{ _popoverId }}"
aria-expanded="false">
{%- if triggerIconOnly %}
<span class="rtds-sr-only">{{ triggerLabel }}</span>
{% render '@icon', { id: triggerIcon | default('mini--information-circle') }, true %}
{%- else %}
{{ triggerLabel }}
{%- endif %}
</button>
<div popover
id="{{ _popoverId }}"
class="rtds-popover__panel"
data-preferred-placement="{{ placement | default('bottom') }}"
{%- if title %} aria-labelledby="{{ _popoverId }}-title"
{%- elif ariaLabel %} aria-label="{{ ariaLabel }}"
{%- endif %}>
<div class="rtds-popover__arrow" aria-hidden="true"></div>
<div data-popover>
{%- if title %}
<div id="{{ _popoverId }}-title" class="rtds-popover__header">
<{{ headingTag | default('h3') }} class="rtds-popover__title">{{ title }}</{{ headingTag | default('h3') }}>
</div>
{%- endif %}
<div class="rtds-popover__body" tabindex="0">
{{ body | safe }}
</div>
{%- if dismissible %}
<button type="button" class="rtds-btn rtds-btn--icon rtds-btn--outlined rtds-border-none rtds-content-01 hover:rtds-bg-black focus:rtds-content-03 rtds-w-8 rtds-h-8 rtds-popover__close">
{% render '@icon', {id: 'mini--x-mark', size: 'rtds-w-4 rtds-h-4'}, true %}
<span class="rtds-sr-only">{{ dismissLabel | default('Chiudi') }}</span>
</button>
{%- endif %}
</div>
</div>
{
"popoverId": "popover-with-close",
"triggerLabel": "Dettagli",
"title": "Titolo pannello",
"headingTag": "h3",
"body": "<p>Contenuto ricco con pulsante di chiusura esplicito.</p>",
"dismissible": true,
"dismissLabel": "Chiudi",
"triggerIconOnly": false,
"triggerIcon": "mini--information-circle",
"placement": "bottom"
}
.rtds-popover__panel {
--_popover-bg: var(--popover-bg, var(--color-background-01, var(--color-neutral-50)));
--_popover-border: var(--popover-border, var(--color-border-gray-01, var(--color-neutral-300)));
--_popover-shadow: var(--popover-shadow, 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1));
--_popover-radius: var(--popover-radius, 0.375rem);
--_popover-width: var(--popover-width, calc(var(--spacing-base) * 80));
--_popover-arrow-size: var(--popover-arrow-size, var(--spacing-2-5, 0.625rem));
background-color: var(--_popover-bg);
border: 1px solid var(--_popover-border);
border-radius: var(--_popover-radius);
box-shadow: var(--_popover-shadow);
width: var(--_popover-width);
max-width: calc(100vw - var(--spacing-8));
padding: var(--spacing-4);
position: absolute;
z-index: 100;
margin: 0;
overflow: visible;
}
.rtds-popover__panel:popover-open {
display: block;
}
.rtds-popover__panel[hidden] {
display: none;
}
.rtds-popover__header {
margin-block-end: var(--spacing-2);
}
.rtds-popover__title {
font-size: var(--fontsize-text-base);
font-weight: 600;
color: var(--color-content-01);
margin: 0;
}
.rtds-popover__body {
color: var(--color-content-01);
font-size: var(--fontsize-text-sm);
outline: none;
}
.rtds-popover__body:focus-visible {
outline: 2px solid var(--color-content-primary);
outline-offset: 2px;
}
.rtds-popover__close {
margin-block-start: var(--spacing-3);
display: flex;
margin-inline-start: auto;
}
/* ================================
Arrow
================================ */
.rtds-popover__arrow {
position: absolute;
width: var(--_popover-arrow-size);
height: var(--_popover-arrow-size);
background: var(--_popover-bg);
border: 1px solid var(--_popover-border);
pointer-events: none;
z-index: -1;
}
/* bottom: panel sotto il trigger → arrow sul bordo superiore, punta verso l'alto */
:where(.rtds-popover__panel[data-placement="bottom"]) .rtds-popover__arrow,
:where(.rtds-popover__panel:not([data-placement])) .rtds-popover__arrow {
top: calc(var(--_popover-arrow-size) / -2);
left: 50%;
transform: translateX(-50%) rotate(45deg);
border-bottom-color: transparent;
border-right-color: transparent;
}
/* top: panel sopra il trigger → arrow sul bordo inferiore, punta verso il basso */
:where(.rtds-popover__panel[data-placement="top"]) .rtds-popover__arrow {
bottom: calc(var(--_popover-arrow-size) / -2);
left: 50%;
transform: translateX(-50%) rotate(45deg);
border-top-color: transparent;
border-left-color: transparent;
}
/* right: panel a destra del trigger → arrow sul bordo sinistro, punta verso sinistra */
:where(.rtds-popover__panel[data-placement="right"]) .rtds-popover__arrow {
left: calc(var(--_popover-arrow-size) / -2);
top: 50%;
transform: translateY(-50%) rotate(45deg);
border-top-color: transparent;
border-right-color: transparent;
}
/* left: panel a sinistra del trigger → arrow sul bordo destro, punta verso destra */
:where(.rtds-popover__panel[data-placement="left"]) .rtds-popover__arrow {
right: calc(var(--_popover-arrow-size) / -2);
top: 50%;
transform: translateY(-50%) rotate(45deg);
border-bottom-color: transparent;
border-left-color: transparent;
}
@layer components {
.rtds-popover__panel {
--_popover-bg: var(--popover-bg, var(--color-background-01, var(--color-neutral-50)));
--_popover-border: var(--popover-border, var(--color-border-gray-01, var(--color-neutral-300)));
--_popover-shadow: var(--popover-shadow, 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1));
--_popover-radius: var(--popover-radius, 0.375rem);
--_popover-width: var(--popover-width, calc(var(--spacing-base) * 80));
--_popover-arrow-size: var(--popover-arrow-size, var(--spacing-2-5, 0.625rem));
background-color: var(--_popover-bg);
border: 1px solid var(--_popover-border);
border-radius: var(--_popover-radius);
box-shadow: var(--_popover-shadow);
width: var(--_popover-width);
max-width: calc(100vw - var(--spacing-8));
padding: var(--spacing-4);
position: absolute;
z-index: 100;
margin: 0;
overflow: visible;
}
.rtds-popover__panel:popover-open {
display: block;
}
.rtds-popover__panel[hidden] {
display: none;
}
.rtds-popover__header {
margin-block-end: var(--spacing-2);
}
.rtds-popover__title {
font-size: var(--fontsize-text-base);
font-weight: 600;
color: var(--color-content-01);
margin: 0;
}
.rtds-popover__body {
color: var(--color-content-01);
font-size: var(--fontsize-text-sm);
outline: none;
}
.rtds-popover__body:focus-visible {
outline: 2px solid var(--color-content-primary);
outline-offset: 2px;
}
.rtds-popover__close {
margin-block-start: var(--spacing-3);
display: flex;
margin-inline-start: auto;
}
/* ================================
Arrow
================================ */
.rtds-popover__arrow {
position: absolute;
width: var(--_popover-arrow-size);
height: var(--_popover-arrow-size);
background: var(--_popover-bg);
border: 1px solid var(--_popover-border);
pointer-events: none;
z-index: -1;
}
/* bottom: panel sotto il trigger → arrow sul bordo superiore, punta verso l'alto */
:where(.rtds-popover__panel[data-placement="bottom"]) .rtds-popover__arrow,
:where(.rtds-popover__panel:not([data-placement])) .rtds-popover__arrow {
top: calc(var(--_popover-arrow-size) / -2);
left: 50%;
transform: translateX(-50%) rotate(45deg);
border-bottom-color: transparent;
border-right-color: transparent;
}
/* top: panel sopra il trigger → arrow sul bordo inferiore, punta verso il basso */
:where(.rtds-popover__panel[data-placement="top"]) .rtds-popover__arrow {
bottom: calc(var(--_popover-arrow-size) / -2);
left: 50%;
transform: translateX(-50%) rotate(45deg);
border-top-color: transparent;
border-left-color: transparent;
}
/* right: panel a destra del trigger → arrow sul bordo sinistro, punta verso sinistra */
:where(.rtds-popover__panel[data-placement="right"]) .rtds-popover__arrow {
left: calc(var(--_popover-arrow-size) / -2);
top: 50%;
transform: translateY(-50%) rotate(45deg);
border-top-color: transparent;
border-right-color: transparent;
}
/* left: panel a sinistra del trigger → arrow sul bordo destro, punta verso destra */
:where(.rtds-popover__panel[data-placement="left"]) .rtds-popover__arrow {
right: calc(var(--_popover-arrow-size) / -2);
top: 50%;
transform: translateY(-50%) rotate(45deg);
border-bottom-color: transparent;
border-left-color: transparent;
}
}
window.addEventListener('load', function () {
var supportsPopover = HTMLElement.prototype.hasOwnProperty('popover');
var supportsAnchorPositioning = CSS.supports('anchor-name: --a');
var unsupportedPopovers = [];
var triggers = document.querySelectorAll('.rtds-popover__trigger[popovertarget]');
if (triggers.length === 0) return;
triggers.forEach(function (trigger) {
var panelId = trigger.getAttribute('popovertarget');
var panel = document.getElementById(panelId);
if (!panel) return;
if (supportsPopover) {
initSupportedPopover(trigger, panel);
} else {
initUnsupportedPopover(trigger, panel);
}
});
// Livello 1 + 2: Popover API nativa
function initSupportedPopover(trigger, panel) {
panel.addEventListener('toggle', function (e) {
trigger.setAttribute('aria-expanded', e.newState === 'open' ? 'true' : 'false');
if (e.newState === 'open') {
positionPanel(trigger, panel, false); // top layer: coordinate viewport
}
});
// Chiudi con ESC e restituisci focus al trigger
panel.addEventListener('keydown', function (e) {
if (e.key === 'Escape') {
panel.hidePopover();
trigger.focus();
}
});
}
// Posizionamento via getBoundingClientRect.
// Level 1/2 (top layer): coordinate viewport, senza offset scroll.
// Level 3 (flusso normale): coordinate documento, con offset scroll.
function getActualPlacement(preferred, rect, panelW, panelH, gap, vw, vh) {
var space = {
bottom: vh - rect.bottom - gap,
top: rect.top - gap,
right: vw - rect.right - gap,
left: rect.left - gap
};
var needed = { bottom: panelH, top: panelH, right: panelW, left: panelW };
if (space[preferred] >= needed[preferred]) return preferred;
var opposite = { bottom: 'top', top: 'bottom', right: 'left', left: 'right' };
var opp = opposite[preferred];
if (space[opp] >= needed[opp]) return opp;
var best = preferred;
var bestSpace = -Infinity;
['bottom', 'top', 'right', 'left'].forEach(function (dir) {
if (space[dir] > bestSpace) { bestSpace = space[dir]; best = dir; }
});
return best;
}
function positionPanel(trigger, panel, useDocumentCoords) {
var preferred = panel.getAttribute('data-preferred-placement') || 'bottom';
var rect = trigger.getBoundingClientRect();
var panelWidth = panel.offsetWidth || 320;
var panelHeight = panel.offsetHeight || 200;
var viewportWidth = window.innerWidth;
var viewportHeight = window.innerHeight;
var gap = 12;
var scrollX = useDocumentCoords ? window.scrollX : 0;
var scrollY = useDocumentCoords ? window.scrollY : 0;
var placement = getActualPlacement(preferred, rect, panelWidth, panelHeight, gap, viewportWidth, viewportHeight);
panel.setAttribute('data-placement', placement);
var top, left;
switch (placement) {
case 'top':
top = rect.top - panelHeight - gap + scrollY;
left = Math.min(
Math.max(rect.left + scrollX, scrollX + 10),
viewportWidth - panelWidth - 10 + scrollX
);
break;
case 'right':
top = Math.min(
Math.max(rect.top + scrollY, scrollY + 10),
viewportHeight - panelHeight - 10 + scrollY
);
left = rect.right + gap + scrollX;
break;
case 'left':
top = Math.min(
Math.max(rect.top + scrollY, scrollY + 10),
viewportHeight - panelHeight - 10 + scrollY
);
left = rect.left - panelWidth - gap + scrollX;
break;
default: // bottom
top = rect.bottom + gap + scrollY;
left = Math.min(
Math.max(rect.left + scrollX, scrollX + 10),
viewportWidth - panelWidth - 10 + scrollX
);
}
// Safety clamp: evita che il panel esca fuori dallo schermo
if (top < scrollY + 10) top = scrollY + 10;
if (left < scrollX + 10) left = scrollX + 10;
if (left + panelWidth > viewportWidth + scrollX - 10) left = viewportWidth - panelWidth - 10 + scrollX;
if (top + panelHeight > viewportHeight + scrollY - 10) top = viewportHeight - panelHeight - 10 + scrollY;
panel.style.top = top + 'px';
panel.style.left = left + 'px';
}
// Livello 3: nessun supporto Popover API
function initUnsupportedPopover(trigger, panel) {
// Inizializza stato chiuso
panel.setAttribute('hidden', '');
trigger.setAttribute('aria-expanded', 'false');
panel.setAttribute('role', 'dialog');
unsupportedPopovers.push({ trigger: trigger, panel: panel });
trigger.addEventListener('click', function (e) {
e.stopPropagation();
var isOpen = !panel.hasAttribute('hidden');
if (isOpen) {
closeUnsupportedPopover(trigger, panel);
} else {
closeAllUnsupportedPopovers(trigger);
openUnsupportedPopover(trigger, panel);
}
});
// ESC
document.addEventListener('keydown', function (e) {
if (e.key === 'Escape' && !panel.hasAttribute('hidden')) {
closeUnsupportedPopover(trigger, panel);
trigger.focus();
}
});
}
function openUnsupportedPopover(trigger, panel) {
panel.removeAttribute('hidden');
trigger.setAttribute('aria-expanded', 'true');
positionPanel(trigger, panel, true); // flusso normale: coordinate documento
}
function closeUnsupportedPopover(trigger, panel) {
panel.setAttribute('hidden', '');
trigger.setAttribute('aria-expanded', 'false');
}
function closeAllUnsupportedPopovers(exceptTrigger) {
unsupportedPopovers.forEach(function (item) {
if (item.trigger !== exceptTrigger) {
closeUnsupportedPopover(item.trigger, item.panel);
}
});
}
// Light dismiss per Livello 3
document.addEventListener('click', function (e) {
unsupportedPopovers.forEach(function (item) {
if (!item.panel.hasAttribute('hidden') &&
!item.panel.contains(e.target) &&
!item.trigger.contains(e.target)) {
closeUnsupportedPopover(item.trigger, item.panel);
}
});
});
// Pulsante close interno al pannello (tutti i livelli)
document.querySelectorAll('.rtds-popover__close').forEach(function (closeBtn) {
closeBtn.addEventListener('click', function () {
var panel = closeBtn.closest('.rtds-popover__panel');
if (!panel) return;
var panelId = panel.id;
var trigger = document.querySelector('[popovertarget="' + panelId + '"]');
if (supportsPopover) {
panel.hidePopover();
} else {
closeUnsupportedPopover(trigger, panel);
}
if (trigger) trigger.focus();
});
});
});
Componente non-modal che mostra contenuto ricco (testo, link, markup HTML) in un pannello sovrapposto, attivato da un click esplicito dell’utente. Diverso dal tooltip, che si apre su hover e contiene solo testo descrittivo.
Il componente produce due elementi indipendenti nel markup — collegati tramite ID, non da un wrapper comune:
<button> — trigger che apre/chiude il pannello; usa l’attributo nativo popovertarget<div popover> — pannello flottante che contiene il contenuto riccoIl posizionamento del pannello è gestito tramite progressive enhancement a 3 livelli (vedi Note).
popover (default): trigger testuale, pannello con titolo visibileno-title: pannello senza titolo visibile; richiede ariaLabel obbligatoriowith-close: aggiunge un pulsante di chiusura esplicito in fondo al pannelloicon-trigger: trigger a icona con label sr-onlyContesto di sviluppo Fractal/Nunjucks — parametri del context
.config.ymlusati nella libreria.
popoverId (stringa, opzionale, default: "popover"): ID condiviso tra trigger e pannello. Deve essere univoco per pagina se il componente appare più volte.triggerLabel (stringa, obbligatorio): Testo del trigger. Diventa sr-only se triggerIconOnly: true.title (stringa, opzionale): Titolo visibile nel pannello. Se presente, genera aria-labelledby sul pannello.headingTag (stringa, opzionale, default: h3): Tag HTML del titolo visibile. Valori: h2–h6.ariaLabel (stringa, opzionale): Nome accessibile del pannello quando title è assente. Obbligatorio se title non viene fornito.body (stringa, obbligatorio): Contenuto HTML del pannello — renderizzato con | safe.dismissible (booleano, opzionale, default: false): Se true, mostra il pulsante di chiusura dopo il body.dismissLabel (stringa, opzionale, default: "Chiudi"): Label sr-only del pulsante di chiusura.placement (stringa, opzionale, default: "bottom"): Placement preferito del panel: bottom | top | left | right. Il JS usa questo come preferenza e fa flip automatico se non c’è spazio.triggerIconOnly (booleano, opzionale, default: false): Se true, la label del trigger è sr-only e l’icona è visibile.triggerIcon (stringa, opzionale, default: "mini--information-circle"): ID dell’icona dallo sprite SVG. Attivo solo con triggerIconOnly: true.Livello AA — criteri pertinenti:
aria-expanded, pannello con nome accessibile<button> — ruolo implicito button; attivabile con Enter e Space; trigger del pannello<div popover> — contenitore del pannello; agisce come dialog nel Livello 3 (attributo role="dialog" aggiunto via JS)| Attributo | Valori | Condizione |
|---|---|---|
aria-expanded |
"true" / "false" |
Sul trigger; aggiornato via JS all’apertura/chiusura |
aria-labelledby |
ID del titolo | Sul pannello; quando title è presente |
aria-label |
stringa | Sul pannello; quando title è assente e ariaLabel è fornito |
Regola nome accessibile: almeno uno tra
titleeariaLabeldeve essere sempre fornito.
| Tasto | Azione |
|---|---|
Tab |
Sposta il focus sul trigger |
Enter / Space |
Apre/chiude il pannello |
Escape |
Chiude il pannello; riporta il focus sul trigger |
Tab (dentro il pannello) |
Naviga gli elementi interattivi nel body |
:focus-visible applicato su .rtds-popover__body per scroll da tastieraaria-expanded — sul trigger; aggiornato via JS; comunica lo stato aperto/chiusoaria-labelledby o aria-label — sul pannello; fornisce il nome accessibile annunciato all’apertura<span class="rtds-sr-only"> — nel trigger icona-only; contiene triggerLabel; l’icona SVG ha aria-hidden="true" implicitamente via @icontabindex="0" sul body — consente lo scroll da tastiera su contenuto lungo.rtds-popover__trigger: Pulsante trigger; nessuno stile base imposto, il consumer applica le classi rtds-btn--* desiderate.rtds-popover__panel: Pannello flottante con sfondo, bordo, ombra e posizionamento assoluto.rtds-popover__header: Intestazione del pannello; contiene il titolo.rtds-popover__title: Titolo visibile nel pannello.rtds-popover__body: Area del contenuto ricco; con tabindex="0" per scroll da tastiera.rtds-popover__close: Pulsante di chiusura esplicito; posizionato in basso a destra del pannello — classi: rtds-btn rtds-btn--icon rtds-btn--outlined rtds-border-none rtds-content-01 hover:rtds-bg-black focus:rtds-content-03 rtds-w-8 rtds-h-8Il componente espone variabili CSS per la personalizzazione dal CMS ospitante. Il CMS può sovrascrivere le variabili Tier 1; se non sovrascritte, usano i token semantici del DS.
| Proprietà CSS | Default DS | Descrizione |
|---|---|---|
--popover-bg |
var(--color-background-01) |
Colore di sfondo del pannello |
--popover-border |
var(--color-border-gray-01) |
Colore del bordo del pannello |
--popover-shadow |
ombra multi-layer | Box shadow del pannello |
--popover-radius |
0.375rem |
Border radius del pannello |
--popover-width |
calc(var(--spacing-base) * 80) |
Larghezza del pannello (default ~20rem) |
--popover-arrow-size |
var(--spacing-2-5) |
Dimensione (width/height) dell’elemento arrow |
| Token DS | Fallback primitivo | Descrizione |
|---|---|---|
--color-background-01 |
var(--color-neutral-50) |
Sfondo principale pagina |
--color-border-gray-01 |
var(--color-neutral-300) |
Bordo sottile grigio |
<!-- Popover con titolo (default) -->
<button type="button"
class="rtds-popover__trigger rtds-btn rtds-btn--secondary"
popovertarget="esempio-1"
aria-expanded="false">
Maggiori informazioni
</button>
<div popover id="esempio-1"
class="rtds-popover__panel"
data-preferred-placement="bottom"
aria-labelledby="esempio-1-title">
<div class="rtds-popover__arrow" aria-hidden="true"></div>
<div data-popover>
<div id="esempio-1-title" class="rtds-popover__header">
<h3 class="rtds-popover__title">Titolo pannello</h3>
</div>
<div class="rtds-popover__body" tabindex="0">
<p>Contenuto ricco con eventuale <a href="#">link</a>.</p>
</div>
</div>
</div>
<!-- Senza titolo (ariaLabel obbligatorio) -->
<button type="button"
class="rtds-popover__trigger rtds-btn rtds-btn--text"
popovertarget="esempio-2"
aria-expanded="false">
Informazioni aggiuntive
</button>
<div popover id="esempio-2"
class="rtds-popover__panel"
data-preferred-placement="bottom"
aria-label="Informazioni aggiuntive">
<div class="rtds-popover__arrow" aria-hidden="true"></div>
<div data-popover>
<div class="rtds-popover__body" tabindex="0">
<p>Contenuto senza heading visibile.</p>
</div>
</div>
</div>
<!-- Con pulsante di chiusura esplicito -->
<div popover id="esempio-3"
class="rtds-popover__panel"
data-preferred-placement="bottom"
aria-labelledby="esempio-3-title">
<div class="rtds-popover__arrow" aria-hidden="true"></div>
<div data-popover>
<div id="esempio-3-title" class="rtds-popover__header">
<h3 class="rtds-popover__title">Titolo pannello</h3>
</div>
<div class="rtds-popover__body" tabindex="0">
<p>Contenuto ricco.</p>
</div>
<button type="button"
class="rtds-btn rtds-btn--icon rtds-btn--outlined rtds-border-none rtds-content-01 hover:rtds-bg-black focus:rtds-content-03 rtds-w-8 rtds-h-8 rtds-popover__close">
<!-- icona mini--x-mark -->
<span class="rtds-sr-only">Chiudi</span>
</button>
</div>
</div>@tooltip) per testo descrittivo su hover; usa il popover per contenuto ricco (link, markup) aperto su click esplicito.title (rtds-popover__title, heading del popover) e ariaLabel (aria-label su elemento popover) deve essere sempre fornito — il pannello senza nome accessibile non è conforme WCAG 4.1.2.popoverId diverso.body devono avere testo descrittivo (non “clicca qui”).popover.js. Browser con supporto nativo alla Popover API usano il Level 1/2; browser senza supporto usano il Level 3 con toggle manuale hidden.role="alert": il popover è contenuto non urgente aperto su azione utente; non usare live region.@icon (variante icon-trigger e pulsante with-close)