Decoded Passage
A single editorial navigation label resolves through a brief stream of typographic noise before settling into its active state. The interaction uses sequential decoding rather than moving or reassembling glyphs, giving a familiar text link a precise, useful response when the local collection is selected.
Live preview
What is Decoded Passage?
A single editorial navigation label briefly passes through controlled typographic noise before resolving into its selected state. The decoding gesture gives a compact text link a memorable response without changing its footprint or relying on per-letter movement.
How it works
The label remains one semantic button. On hover and keyboard focus, its characters briefly cycle through a restrained glyph set before decoding back to the current phrase. Activating it toggles the local selection state, changes the label, and reveals a concise status message. The transition is driven by requestAnimationFrame and stops cleanly at the final copy.
Where to use it
- Compact editorial navigation labels
- Portfolio or collection indexes with local selection feedback
- Design-system specimens for text-only interaction states
Accessibility
The focal element is a native button with an accessible label and keyboard support. The selected state is communicated through color, text, and a visible status message announced with aria-live. Reduced-motion preferences remove decorative CSS transitions; the semantic action remains fully available.
Performance
The component uses one button, one status node, a small requestAnimationFrame loop, and no external assets or libraries. Animation is cancelled before a new decoding pass begins.
Source code
Explore and copy the original source. Interactive previews apply localized labels, while source code keeps the original strings (usually English). For translated dynamic labels in a project, use AuroraLabI18n.t(key, fallback).
<main class="stage">
<button class="decode-label" type="button" data-decode data-ali-i18n="copy_1" data-ali-i18n-aria-label="copy_1" aria-label="Browse collection">Browse collection</button>
<p class="decode-status" data-status data-ali-i18n="copy_2" aria-live="polite" aria-atomic="true"> </p>
</main>
:root{
--bg:#0D0B0F;
--surface:#16131A;
--fg:#EEEAE4;
--muted:#A49DAB;
--violet:#62447D;
--raspberry:#DF284D;
--gold:#F5BA09;
--lilac:#B79BD4;
}
*{box-sizing:border-box}
html,body{margin:0;min-width:100%;min-height:100%;background:var(--bg)}
body{
color:var(--fg);
font-family:system-ui,-apple-system,"Segoe UI",sans-serif;
}
.stage{
width:100%;
min-height:100vh;
min-height:100svh;
display:flex;
align-items:center;
justify-content:flex-start;
padding:clamp(28px,9vw,116px) clamp(24px,13vw,190px);
background:var(--bg);
}
.decode-label{
position:relative;
display:inline-block;
max-width:100%;
margin:0;
padding:0 0 .08em;
border:0;
border-bottom:1px solid color-mix(in srgb,var(--fg) 30%,transparent);
color:var(--fg);
background:transparent;
cursor:pointer;
font-family:Georgia,"Times New Roman",serif;
font-size:clamp(2.15rem,6vw,5.7rem);
font-weight:400;
letter-spacing:-.055em;
line-height:.98;
text-align:left;
transition:color .25s ease,border-color .25s ease;
}
.decode-label::before{
content:"";
position:absolute;
left:0;
bottom:-2px;
width:2.6rem;
height:3px;
background:var(--gold);
transform-origin:left center;
transition:width .45s cubic-bezier(.2,.8,.2,1),background-color .25s ease;
}
.decode-label:hover,
.decode-label:focus-visible{color:var(--lilac);border-bottom-color:var(--lilac)}
.decode-label:hover::before,
.decode-label:focus-visible::before{width:100%;background:var(--raspberry)}
.decode-label:focus-visible{outline:1px solid var(--gold);outline-offset:12px}
.decode-label.is-active{color:var(--gold)}
.decode-label.is-active::before{width:100%;background:var(--raspberry)}
.decode-status{
position:absolute;
margin:0;
margin-left:clamp(0px,31vw,430px);
align-self:center;
max-width:12rem;
color:var(--muted);
font-family:ui-monospace,SFMono-Regular,Menlo,monospace;
font-size:clamp(.68rem,1.2vw,.82rem);
letter-spacing:.08em;
line-height:1.35;
text-transform:uppercase;
opacity:0;
transform:translateY(5px);
transition:opacity .35s ease,transform .35s ease;
}
.decode-status.is-visible{opacity:1;transform:none}
@media (max-width:620px){
.stage{display:block;padding:43vh 24px 0}
.decode-label{font-size:clamp(2.25rem,12vw,4rem)}
.decode-status{
position:static;
margin:1.45rem 0 0;
max-width:10rem;
}
}
@media (prefers-reduced-motion:reduce){
.decode-label,.decode-label::before,.decode-status{transition:none}
}
var trigger = document.querySelector('[data-decode]');
var status = document.querySelector('[data-status]');
if (trigger && status) {
var glyphs = '!<>-_/?[]{}=+*^#%&';
var active = false;
var frameId = 0;
var timerId = 0;
var translate = function (key, fallback) {
return window.AuroraLabI18n && typeof window.AuroraLabI18n.t === 'function'
? window.AuroraLabI18n.t(key, fallback)
: fallback;
};
var decode = function (target) {
cancelAnimationFrame(frameId);
window.clearTimeout(timerId);
var source = trigger.textContent;
var length = Math.max(source.length, target.length);
var frame = 0;
var total = 16;
var tick = function () {
var progress = Math.min(frame / total, 1);
var resolved = Math.floor(progress * length);
var output = '';
for (var i = 0; i < length; i += 1) {
if (i < resolved) {
output += target.charAt(i) || '';
} else if (target.charAt(i) === ' ') {
output += ' ';
} else {
output += glyphs.charAt((i * 7 + frame * 3) % glyphs.length);
}
}
trigger.textContent = output;
frame += 1;
if (progress < 1) {
frameId = window.requestAnimationFrame(tick);
} else {
trigger.textContent = target;
}
};
tick();
};
trigger.addEventListener('mouseenter', function () {
decode(active ? translate('copy_2', 'Collection selected') : translate('copy_1', 'Browse collection'));
});
trigger.addEventListener('focus', function () {
decode(active ? translate('copy_2', 'Collection selected') : translate('copy_1', 'Browse collection'));
});
trigger.addEventListener('click', function () {
active = !active;
var next = active ? translate('copy_2', 'Collection selected') : translate('copy_1', 'Browse collection');
decode(next);
trigger.classList.toggle('is-active', active);
status.classList.toggle('is-visible', active);
status.textContent = active ? translate('copy_2', 'Collection selected') : '\u00a0';
trigger.setAttribute('aria-label', next);
});
}