.sr-only{position:absolute;width:1px;height:1px;padding:0;margin:-1px;overflow:hidden;clip:rect(0,0,0,0);white-space:nowrap;border:0}
/* Social icon hover effects. Each link is named by aria-label (or by its hidden and revealed text in the
expanding version); icons are aria-hidden. Every effect runs on :hover and :focus-visible, and under
prefers-reduced-motion the state changes without moving. */
.fx-row{display:flex;flex-wrap:wrap;align-items:center;gap:14px;margin:0;padding:0;list-style:none}
.fx{display:grid;place-items:center;text-decoration:none;
transition:color .25s,background-color .25s,border-color .25s,transform .25s,box-shadow .25s}
.fx svg{width:20px;height:20px;fill:currentColor}
.fx:focus-visible{outline:3px solid #0f172a;outline-offset:3px}
.fx-row.fx-lifts{gap:16px;padding:8px 0 12px}
/* 1. fill: a ::before layer scales up from the bottom edge */
.fx-fill{position:relative;overflow:hidden;width:48px;height:48px;border:1.5px solid #cbd5e1;border-radius:50%;color:#334155}
.fx-fill::before{content:"";position:absolute;inset:0;background:var(--fx-brand);transform:scaleY(0);transform-origin:bottom;transition:transform .3s cubic-bezier(.2,.7,.2,1)}
.fx-fill svg{position:relative}
.fx-fill:hover,.fx-fill:focus-visible{color:var(--fx-on);border-color:var(--fx-brand)}
.fx-fill:hover::before,.fx-fill:focus-visible::before{transform:scaleY(1)}
/* 2. slide: two faces stacked in a track that moves up by one face */
.fx-slide{overflow:hidden;width:48px;height:48px;border-radius:12px;place-items:start}
.fx-track{display:flex;flex-direction:column;transition:transform .3s cubic-bezier(.2,.7,.2,1)}
.fx-face{display:grid;place-items:center;width:48px;height:48px;background:#e2e8f0;color:#334155}
.fx-face+.fx-face{background:var(--fx-brand);color:var(--fx-on)}
.fx-slide:hover .fx-track,.fx-slide:focus-visible .fx-track{transform:translateY(-48px)}
/* 3. lift */
.fx-lift{position:relative;width:52px;height:52px;border-radius:14px;background:#fff;color:#334155;
box-shadow:0 1px 2px rgba(15,23,42,.12),0 0 0 1px #e2e8f0}
.fx-lift::after{content:"";position:absolute;left:50%;bottom:-10px;width:0;height:3px;border-radius:3px;background:var(--fx-brand);
transform:translateX(-50%);transition:width .25s}
.fx-lift:hover,.fx-lift:focus-visible{transform:translateY(-6px);color:var(--fx-ink);box-shadow:0 14px 24px -10px rgba(15,23,42,.4),0 0 0 1px #e2e8f0}
.fx-lift:hover::after,.fx-lift:focus-visible::after{width:28px}
/* 4. expand: the name sits in a span with max-width:0 that opens on hover and focus */
.fx-expand{display:inline-flex;align-items:center;height:48px;padding:0 14px;border-radius:999px;background:#0f172a;color:#fff;
font-weight:600;font-size:.92rem;white-space:nowrap}
.fx-name{display:inline-block;max-width:0;overflow:hidden;transition:max-width .35s cubic-bezier(.2,.7,.2,1),margin .35s}
.fx-expand:hover,.fx-expand:focus-visible{background:var(--fx-brand)}
.fx-expand:hover .fx-name,.fx-expand:focus-visible .fx-name{max-width:8em;margin-left:10px}
@media (prefers-reduced-motion:reduce){
.fx,.fx::before,.fx::after,.fx-track,.fx-name{transition:none}
.fx-lift:hover,.fx-lift:focus-visible{transform:none}
}
.fx-behance{--fx-brand:#1769FF;--fx-on:#ffffff;--fx-ink:#1769FF}
.fx-bluesky{--fx-brand:#1185FE;--fx-on:#ffffff;--fx-ink:#1185FE}
.fx-discord{--fx-brand:#5865F2;--fx-on:#ffffff;--fx-ink:#5865F2}
.fx-dribbble{--fx-brand:#EA4C89;--fx-on:#ffffff;--fx-ink:#EA4C89}
.fx-email{--fx-brand:#0F766E;--fx-on:#ffffff;--fx-ink:#0F766E}
.fx-facebook{--fx-brand:#0866FF;--fx-on:#ffffff;--fx-ink:#0866FF}
.fx-github{--fx-brand:#181717;--fx-on:#ffffff;--fx-ink:#181717}
.fx-instagram{--fx-brand:#FF0069;--fx-on:#ffffff;--fx-ink:#FF0069}
.fx-linkedin{--fx-brand:#0A66C2;--fx-on:#ffffff;--fx-ink:#0A66C2}
.fx-mastodon{--fx-brand:#6364FF;--fx-on:#ffffff;--fx-ink:#6364FF}
.fx-pinterest{--fx-brand:#BD081C;--fx-on:#ffffff;--fx-ink:#BD081C}
.fx-reddit{--fx-brand:#FF4500;--fx-on:#ffffff;--fx-ink:#FF4500}
.fx-rss{--fx-brand:#FFA500;--fx-on:#0b0f17;--fx-ink:#0f172a}
.fx-telegram{--fx-brand:#26A5E4;--fx-on:#0b0f17;--fx-ink:#0f172a}
.fx-threads{--fx-brand:#000000;--fx-on:#ffffff;--fx-ink:#000000}
.fx-tiktok{--fx-brand:#000000;--fx-on:#ffffff;--fx-ink:#000000}
.fx-whatsapp{--fx-brand:#25D366;--fx-on:#0b0f17;--fx-ink:#0f172a}
.fx-x{--fx-brand:#000000;--fx-on:#ffffff;--fx-ink:#000000}
.fx-youtube{--fx-brand:#FF0000;--fx-on:#ffffff;--fx-ink:#FF0000}
/* Social icon hover effects (Bootstrap). Bootstrap has no hover effects of this kind, so the effects are the
same CSS as the plain version on .btn links (with --bs-btn-* variables neutralised), plus .visually-hidden,
.shadow-sm and flex utilities. Each link is named by aria-label or by its text. */
.fx-row{gap:14px}
.fx-row.fx-lifts{gap:16px;padding:8px 0 12px}
.fx.btn{--bs-btn-padding-x:0;--bs-btn-padding-y:0;--bs-btn-border-width:0;--bs-btn-hover-color:inherit;--bs-btn-active-color:inherit;
--bs-btn-hover-bg:transparent;--bs-btn-active-bg:transparent;--bs-btn-active-border-color:transparent;--bs-btn-hover-border-color:transparent;
--bs-btn-line-height:1;display:grid;place-items:center;
transition:color .25s,background-color .25s,border-color .25s,transform .25s,box-shadow .25s}
.fx svg{width:20px;height:20px;fill:currentColor}
.fx:focus-visible{outline:3px solid #0f172a;outline-offset:3px;box-shadow:none}
.fx-fill.btn{border:1.5px solid #cbd5e1}
.fx-expand.btn{--bs-btn-color:#fff;--bs-btn-hover-color:#fff;--bs-btn-active-color:#fff;display:inline-flex}
/* 1. fill: a ::before layer scales up from the bottom edge */
.fx-fill{position:relative;overflow:hidden;width:48px;height:48px;border:1.5px solid #cbd5e1;border-radius:50%;color:#334155}
.fx-fill::before{content:"";position:absolute;inset:0;background:var(--fx-brand);transform:scaleY(0);transform-origin:bottom;transition:transform .3s cubic-bezier(.2,.7,.2,1)}
.fx-fill svg{position:relative}
.fx-fill:hover,.fx-fill:focus-visible{color:var(--fx-on);border-color:var(--fx-brand)}
.fx-fill:hover::before,.fx-fill:focus-visible::before{transform:scaleY(1)}
/* 2. slide: two faces stacked in a track that moves up by one face */
.fx-slide{overflow:hidden;width:48px;height:48px;border-radius:12px;place-items:start}
.fx-track{display:flex;flex-direction:column;transition:transform .3s cubic-bezier(.2,.7,.2,1)}
.fx-face{display:grid;place-items:center;width:48px;height:48px;background:#e2e8f0;color:#334155}
.fx-face+.fx-face{background:var(--fx-brand);color:var(--fx-on)}
.fx-slide:hover .fx-track,.fx-slide:focus-visible .fx-track{transform:translateY(-48px)}
/* 3. lift */
.fx-lift{position:relative;width:52px;height:52px;border-radius:14px;background:#fff;color:#334155;
box-shadow:0 1px 2px rgba(15,23,42,.12),0 0 0 1px #e2e8f0}
.fx-lift::after{content:"";position:absolute;left:50%;bottom:-10px;width:0;height:3px;border-radius:3px;background:var(--fx-brand);
transform:translateX(-50%);transition:width .25s}
.fx-lift:hover,.fx-lift:focus-visible{transform:translateY(-6px);color:var(--fx-ink);box-shadow:0 14px 24px -10px rgba(15,23,42,.4),0 0 0 1px #e2e8f0}
.fx-lift:hover::after,.fx-lift:focus-visible::after{width:28px}
/* 4. expand: the name sits in a span with max-width:0 that opens on hover and focus */
.fx-expand{display:inline-flex;align-items:center;height:48px;padding:0 14px;border-radius:999px;background:#0f172a;color:#fff;
font-weight:600;font-size:.92rem;white-space:nowrap}
.fx-name{display:inline-block;max-width:0;overflow:hidden;transition:max-width .35s cubic-bezier(.2,.7,.2,1),margin .35s}
.fx-expand:hover,.fx-expand:focus-visible{background:var(--fx-brand)}
.fx-expand:hover .fx-name,.fx-expand:focus-visible .fx-name{max-width:8em;margin-left:10px}
@media (prefers-reduced-motion:reduce){
.fx,.fx::before,.fx::after,.fx-track,.fx-name{transition:none}
.fx-lift:hover,.fx-lift:focus-visible{transform:none}
}
.fx-behance{--fx-brand:#1769FF;--fx-on:#ffffff;--fx-ink:#1769FF}
.fx-bluesky{--fx-brand:#1185FE;--fx-on:#ffffff;--fx-ink:#1185FE}
.fx-discord{--fx-brand:#5865F2;--fx-on:#ffffff;--fx-ink:#5865F2}
.fx-dribbble{--fx-brand:#EA4C89;--fx-on:#ffffff;--fx-ink:#EA4C89}
.fx-email{--fx-brand:#0F766E;--fx-on:#ffffff;--fx-ink:#0F766E}
.fx-facebook{--fx-brand:#0866FF;--fx-on:#ffffff;--fx-ink:#0866FF}
.fx-github{--fx-brand:#181717;--fx-on:#ffffff;--fx-ink:#181717}
.fx-instagram{--fx-brand:#FF0069;--fx-on:#ffffff;--fx-ink:#FF0069}
.fx-linkedin{--fx-brand:#0A66C2;--fx-on:#ffffff;--fx-ink:#0A66C2}
.fx-mastodon{--fx-brand:#6364FF;--fx-on:#ffffff;--fx-ink:#6364FF}
.fx-pinterest{--fx-brand:#BD081C;--fx-on:#ffffff;--fx-ink:#BD081C}
.fx-reddit{--fx-brand:#FF4500;--fx-on:#ffffff;--fx-ink:#FF4500}
.fx-rss{--fx-brand:#FFA500;--fx-on:#0b0f17;--fx-ink:#0f172a}
.fx-telegram{--fx-brand:#26A5E4;--fx-on:#0b0f17;--fx-ink:#0f172a}
.fx-threads{--fx-brand:#000000;--fx-on:#ffffff;--fx-ink:#000000}
.fx-tiktok{--fx-brand:#000000;--fx-on:#ffffff;--fx-ink:#000000}
.fx-whatsapp{--fx-brand:#25D366;--fx-on:#0b0f17;--fx-ink:#0f172a}
.fx-x{--fx-brand:#000000;--fx-on:#ffffff;--fx-ink:#000000}
.fx-youtube{--fx-brand:#FF0000;--fx-on:#ffffff;--fx-ink:#FF0000}
Four animated hover effects for social icons, all driven by CSS transitions. Fill grows the network’s colour up from the bottom of an outlined circle with a scaled ::before layer, so the icon seems to be dipped in colour. Slide stacks two copies of the icon in a track inside a rounded tile, a grey one and a brand-coloured one, and moves the track up by one tile on hover so the brand version pushes the grey one out. Lift raises a white tile by six pixels, deepens its shadow and draws a short brand-coloured bar under it. Expand starts as a dark circle and opens into a pill that shows the network’s name, by animating the max-width of the name. Every effect also runs on :focus-visible, so keyboard users see the same thing as mouse users. Under prefers-reduced-motion the transitions are removed and nothing moves or lifts, but the colour change still marks the active icon. The Bootstrap version applies the same effect CSS to .btn links, since Bootstrap has no equivalent, and the Tailwind version uses before:, after:, group-hover: and motion-reduce: utilities. No JavaScript.
What’s included
Fill, slide, lift and expand effects
Effects run on keyboard focus as well as hover
Reduced motion keeps the colour change and drops the movement
Expand variant reveals real text that is also the accessible name
Tailwind version uses before:, after:, group-hover: and motion-reduce:
Accessibility
Icon links are named by aria-label, such as uiCookies on YouTube, with aria-hidden SVGs; in the expanding variant the revealed name is part of the link text, completed by hidden words. Every effect triggers on :focus-visible as well as hover, focus also shows a 3px outline, and prefers-reduced-motion removes all movement.
Customise it
Change the easing and durations in the transition values, the 48 pixel sizes in .fx-fill, .fx-slide and .fx-face, and the brand colours in the .fx-network classes. In Tailwind edit duration-300, the size utilities and the [--b] property.