Hinged Margin Note
Una nota editoriale compatta si comporta come una piega di carta: attivando la linguetta sottile, il dettaglio nascosto si apre lungo il bordo rigato. L’interazione mostra il contesto direttamente sul posto, conserva l’ingombro dell’elemento e mantiene il movimento chiaro, senza effetti superflui.
Anteprima interattiva
Cos’è Hinged Margin Note?
Un’annotazione editoriale compatta si apre come una piega di carta. Una linguetta laterale fa emergere una nota breve sotto il bordo, creando un dettaglio consultabile senza trasformare il componente in una sezione completa.
Come è costruito
All’inizio la nota appare come un frammento discreto delimitato da una linea verticale. Al clic, il contenuto sottostante si espande da una griglia ritagliata e una rotazione prospettica accompagna la superficie fino alla posizione finale. Il controllo resta un pulsante nativo con stato di espansione esplicito.
Ideale per
- Definizioni brevi accanto a testi editoriali
- Annotazioni compatte nella documentazione
- Note contestuali negli indici di portfolio
Accessibilità
Usa un pulsante nativo, un focus da tastiera ben visibile, lo stato aria-expanded e un collegamento aria-controls. Quando la nota è chiusa, il contenuto non viene esposto alla tecnologia assistiva. Il movimento rispetta la preferenza per la riduzione delle animazioni.
Prestazioni
Utilizza soltanto trasformazioni CSS, opacità e interpolazione della griglia, senza immagini, librerie, canvas o richieste di rete. L’interazione dipende da un solo listener e resta leggera anche sui dispositivi touch.
Codice sorgente
Esplora, copia e riutilizza HTML, CSS e JavaScript originali.
<main class="showcase" aria-label="Interactive component preview">
<div class="inner-wrap">
<section class="hinge-note" data-motion="on">
<div class="note-paper" aria-live="polite">
<p class="note-copy" data-ali-i18n="copy_2">A detail can unfold without taking over the page.</p>
</div>
<button class="hinge-trigger" type="button" aria-expanded="false" aria-controls="hinge-detail">
<span class="trigger-open" data-ali-i18n="copy_1">Open note</span>
<span class="trigger-close" data-ali-i18n="copy_3">Close note</span>
<span class="hinge-mark" aria-hidden="true">↗</span>
</button>
<div id="hinge-detail" class="hinge-detail" hidden>
<div class="detail-rule" aria-hidden="true"></div>
<p data-ali-i18n="copy_4">Useful context, kept close.</p>
</div>
</section>
</div>
</main>
:root {
--bg: #F1EDE6;
--surface: #EEEAE4;
--ink: #16131A;
--muted: #5E5765;
--accent: #62447D;
--accent-soft: #C4B6D3;
--signal: #DF284D;
--fold-depth: 18deg;
--fold-speed: 520ms;
}
* { box-sizing: border-box; }
body {
margin: 0;
min-width: 320px;
background: var(--bg);
color: var(--ink);
font-family: Georgia, "Times New Roman", serif;
}
.showcase {
min-height: 100vh;
min-height: 640px;
display: grid;
place-items: center;
overflow: hidden;
background: var(--bg);
padding: clamp(28px, 8vw, 88px) clamp(18px, 7vw, 96px);
}
.inner-wrap {
width: min(100%, 560px);
display: grid;
place-items: center;
}
.hinge-note {
width: min(100%, 420px);
position: relative;
padding-left: clamp(24px, 6vw, 42px);
border-left: 1px solid var(--ink);
}
.hinge-note::before {
content: "01";
position: absolute;
left: 0;
top: -1.2em;
transform: translateX(-50%);
padding: 0 .35rem;
background: var(--bg);
color: var(--muted);
font: 600 .68rem/1.2 ui-monospace, SFMono-Regular, Menlo, monospace;
letter-spacing: .12em;
}
.note-paper {
width: min(100%, 310px);
min-height: 44px;
display: grid;
align-items: center;
padding: 12px 16px;
background: var(--surface);
border: 1px solid color-mix(in srgb, var(--ink) 22%, transparent);
border-left: 3px solid var(--accent);
box-shadow: 5px 5px 0 color-mix(in srgb, var(--accent-soft) 52%, transparent);
}
.note-copy {
margin: 0;
color: var(--muted);
font-size: clamp(.78rem, 1.7vw, .92rem);
line-height: 1.45;
}
.hinge-trigger {
position: relative;
z-index: 2;
display: inline-flex;
align-items: center;
gap: .7rem;
margin-top: 18px;
padding: 0;
border: 0;
background: transparent;
color: var(--ink);
cursor: pointer;
font: 600 clamp(1.02rem, 2.4vw, 1.3rem)/1.1 Georgia, "Times New Roman", serif;
text-align: left;
}
.hinge-trigger::after {
content: "";
position: absolute;
right: 1.55rem;
bottom: -7px;
left: 0;
height: 1px;
background: var(--accent);
transform-origin: left;
transition: transform var(--fold-speed) cubic-bezier(.2,.8,.2,1);
}
.hinge-trigger:hover::after,
.hinge-trigger:focus-visible::after { transform: scaleX(.72); }
.hinge-trigger:focus-visible {
outline: 2px solid var(--signal);
outline-offset: 5px;
}
.hinge-mark {
display: inline-grid;
place-items: center;
width: 1.55rem;
height: 1.55rem;
border: 1px solid var(--accent);
color: var(--accent);
font: 500 .9rem/1 ui-monospace, SFMono-Regular, Menlo, monospace;
transition: transform var(--fold-speed) cubic-bezier(.2,.8,.2,1), background var(--fold-speed) ease;
}
.hinge-note.is-open .hinge-mark {
transform: rotate(90deg);
background: var(--accent-soft);
}
.trigger-close { display: none; }
.hinge-note.is-open .trigger-open { display: none; }
.hinge-note.is-open .trigger-close { display: inline; }
.hinge-detail {
display: grid;
grid-template-rows: 0fr;
transform-origin: top left;
transform: perspective(500px) rotateX(var(--fold-depth));
opacity: 0;
transition: grid-template-rows var(--fold-speed) cubic-bezier(.2,.8,.2,1), transform var(--fold-speed) cubic-bezier(.2,.8,.2,1), opacity 220ms ease;
}
.hinge-detail > * { min-height: 0; overflow: hidden; }
.hinge-note.is-open .hinge-detail {
grid-template-rows: 1fr;
transform: perspective(500px) rotateX(0deg);
opacity: 1;
}
.detail-rule {
height: 1px;
margin-top: 18px;
background: var(--accent);
}
.hinge-detail p {
margin: 9px 0 0;
color: var(--muted);
font: .72rem/1.4 ui-monospace, SFMono-Regular, Menlo, monospace;
letter-spacing: .04em;
}
@media (max-width: 430px) {
.showcase { min-height: 100vh; padding: 34px 22px; }
.hinge-note { padding-left: 24px; }
.note-paper { width: 100%; }
}
@media (prefers-reduced-motion: reduce) {
*, *::before, *::after { transition-duration: 1ms !important; }
}
(() => {
const root = document.querySelector('.hinge-note');
const trigger = root?.querySelector('.hinge-trigger');
const detail = root?.querySelector('#hinge-detail');
if (!root || !trigger || !detail) return;
trigger.addEventListener('click', () => {
const isOpen = root.classList.toggle('is-open');
trigger.setAttribute('aria-expanded', String(isOpen));
detail.hidden = !isOpen;
const openLabel = root.querySelector('.trigger-open');
const closeLabel = root.querySelector('.trigger-close');
if (window.AuroraLabI18n?.t) {
if (openLabel) openLabel.textContent = window.AuroraLabI18n.t('copy_1', 'Open note');
if (closeLabel) closeLabel.textContent = window.AuroraLabI18n.t('copy_3', 'Close note');
}
});
})();