A set of Material design inspired tooltips. Made with Ai support. Base of CSS with a sprinkle of JS.
Fonts & Icons from https://fonts.google.com
A Pen by Stan Williams on CodePen.
A set of Material design inspired tooltips. Made with Ai support. Base of CSS with a sprinkle of JS.
Fonts & Icons from https://fonts.google.com
A Pen by Stan Williams on CodePen.
| <header> | |
| <h1>CSS Tooltips</h1> | |
| <p>Just CSS ::before (bubble) and ::after (arrow). And a sprinkle of JavaScript.</p> | |
| </header> | |
| <div class="section"> | |
| <div class="section-label">01 — Positions</div> | |
| <div class="demo-grid"> | |
| <button class="btn" data-tip="I appear on top" data-pos="top">Top</button> | |
| <button class="btn" data-tip="I appear below" data-pos="bottom">Bottom</button> | |
| <button class="btn" data-tip="I appear left" data-pos="left">Left</button> | |
| <button class="btn" data-tip="I appear right" data-pos="right">Right</button> | |
| </div> | |
| </div> | |
| <div class="section"> | |
| <div class="section-label">02 — Variants</div> | |
| <div class="demo-grid"> | |
| <button class="btn" data-tip="Default style">Default</button> | |
| <button class="btn" data-tip="Highlighted action" data-variant="accent">Accent</button> | |
| <button class="btn" data-tip="This will delete everything" data-variant="danger">Danger</button> | |
| <button class="btn" data-tip="More info available" data-variant="info">Info</button> | |
| </div> | |
| </div> | |
| <div class="section"> | |
| <div class="section-label">03 — Icon buttons</div> | |
| <div class="demo-grid"> | |
| <span class="icon-btn" tabindex="0" data-tip="Copy to clipboard"><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M620-163 450-333l56-56 114 114 226-226 56 56-282 282Zm220-397h-80v-200h-80v120H280v-120h-80v560h240v80H200q-33 0-56.5-23.5T120-200v-560q0-33 23.5-56.5T200-840h167q11-35 43-57.5t70-22.5q40 0 71.5 22.5T594-840h166q33 0 56.5 23.5T840-760v200ZM508.5-771.5Q520-783 520-800t-11.5-28.5Q497-840 480-840t-28.5 11.5Q440-817 440-800t11.5 28.5Q463-760 480-760t28.5-11.5Z"/></svg></span> | |
| <span class="icon-btn" tabindex="0" data-tip="Download" data-pos="bottom"><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M480-320 280-520l56-58 104 104v-326h80v326l104-104 56 58-200 200ZM240-160q-33 0-56.5-23.5T160-240v-120h80v120h480v-120h80v120q0 33-23.5 56.5T720-160H240Z"/></svg></span> | |
| <span class="icon-btn" tabindex="0" data-tip="Share link" data-pos="right"><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="M318-120q-82 0-140-58t-58-140q0-40 15-76t43-64l134-133 56 56-134 134q-17 17-25.5 38.5T200-318q0 49 34.5 83.5T318-200q23 0 45-8.5t39-25.5l133-134 57 57-134 133q-28 28-64 43t-76 15Zm79-220-57-57 223-223 57 57-223 223Zm251-28-56-57 134-133q17-17 25-38t8-44q0-50-34-85t-84-35q-23 0-44.5 8.5T558-726L425-592l-57-56 134-134q28-28 64-43t76-15q82 0 139.5 58T839-641q0 39-14.5 75T782-502L648-368Z"/></svg></span> | |
| <span class="icon-btn" tabindex="0" data-tip="Delete permanently" data-variant="danger">✕</span> | |
| <span class="icon-btn" tabindex="0" data-tip="Settings" data-pos="left">⚙</span> | |
| <span class="icon-btn" tabindex="0" data-tip="Bookmark this" data-variant="accent"><svg xmlns="http://www.w3.org/2000/svg" height="24px" viewBox="0 -960 960 960" width="24px" fill="#e3e3e3"><path d="m438-400 198-198-57-56-141 141-57-57-57 57 114 113ZM200-120v-640q0-33 23.5-56.5T280-840h400q33 0 56.5 23.5T760-760v640L480-240 200-120Zm80-122 200-86 200 86v-518H280v518Zm0-518h400-400Z"/></svg></span> | |
| </div> | |
| </div> | |
| <div class="section"> | |
| <div class="section-label">04 — Inline text tooltips</div> | |
| <p class="prose"> | |
| Pseudo-elements are | |
| <span tabindex="0" data-tip="::before and ::after — they exist in the render tree but not the DOM">ghost elements</span> | |
| generated by CSS. They require a | |
| <span tabindex="0" data-tip="Even an empty string content: '' works for shape-based tricks">content property</span> | |
| to render, and each element gets exactly | |
| <span tabindex="0" data-tip="One ::before and one ::after — that's your lot">two of them</span>. | |
| Together they're enough to build a complete | |
| <span tabindex="0" data-tip="::before is the bubble, ::after is the arrow nub" data-variant="accent">tooltip system</span> | |
| with zero extra markup. | |
| </p> | |
| </div> | |
| <p class="note"><span>::before</span> = bubble · <span>::after</span> = arrow · no JS, no extra HTML</p> |
| document.addEventListener("click", (e) => { | |
| if (!e.target.closest("[data-tip]")) { | |
| document.activeElement.blur(); | |
| } | |
| }); |
| @import url ('https://fonts.googleapis.com/css2?family=DM+Mono:ital,wght@0,300;0,400;0,500;1,300&family=DM+Sans:wght@300;400;500;600&display=swap') | |
| *, *::before, *::after { | |
| box-sizing: border-box; | |
| margin: 0; | |
| padding: 0; | |
| } | |
| :root { | |
| --bg: #0d0d0d; | |
| --surface: #161616; | |
| --border: #2a2a2a; | |
| --text: #e8e8e8; | |
| --muted: #666; | |
| --accent: #e8ff47; | |
| --accent-dim: rgba(232, 255, 71, 0.12); | |
| --tip-bg: #1e1e1e; | |
| --tip-text: #f0f0f0; | |
| --tip-border: #333; | |
| --radius: 70px; | |
| --radius1: 15px; | |
| } | |
| body { | |
| background: var(--bg); | |
| color: var(--text); | |
| font-family: 'DM Sans', sans-serif; | |
| min-height: 100vh; | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| justify-content: center; | |
| padding: 60px 24px; | |
| gap: 64px; | |
| } | |
| header { | |
| text-align: center; | |
| } | |
| header h1 { | |
| font-family: 'DM Mono', monospace; | |
| font-weight: 400; | |
| font-size: clamp(1.1rem, 3vw, 1.5rem); | |
| letter-spacing: 0.08em; | |
| color: var(--accent); | |
| } | |
| header p { | |
| margin-top: 10px; | |
| font-size: 0.85rem; | |
| color: var(--muted); | |
| font-weight: 200; | |
| } | |
| .section { | |
| width: 100%; | |
| max-width: 700px; | |
| display: flex; | |
| flex-direction: column; | |
| gap: 20px; | |
| } | |
| .section-label { | |
| font-family: 'DM Mono', monospace; | |
| font-size: 0.65rem; | |
| letter-spacing: 0.2em; | |
| text-transform: uppercase; | |
| color: var(--muted); | |
| border-bottom: 1px solid var(--border); | |
| padding-bottom: 8px; | |
| } | |
| .demo-grid { | |
| display: flex; | |
| flex-wrap: wrap; | |
| gap: 16px; | |
| justify-content: center; | |
| } | |
| [data-tip] { | |
| position: relative; | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| cursor: default; | |
| } | |
| [data-tip]::before { | |
| content: attr(data-tip); | |
| position: absolute; | |
| background: var(--tip-bg); | |
| color: var(--tip-text); | |
| font-family: 'DM Mono', monospace; | |
| font-size: 0.72rem; | |
| font-weight: 400; | |
| white-space: nowrap; | |
| padding: 6px 12px; | |
| border-radius: var(--radius1); | |
| border: 1px solid var(--tip-border); | |
| pointer-events: none; | |
| opacity: 0; | |
| transform: translateY(4px); | |
| transition: opacity 0.18s ease, transform 0.18s ease; | |
| z-index: 10; | |
| box-shadow: 0 8px 24px rgba(0,0,0,0.5); | |
| } | |
| [data-tip]::after { | |
| content: ''; | |
| position: absolute; | |
| pointer-events: none; | |
| opacity: 0; | |
| transition: opacity 0.18s ease, transform 0.18s ease; | |
| z-index: 11; | |
| width: 0; | |
| height: 0; | |
| } | |
| [data-tip]:hover::before, | |
| [data-tip]:focus::before, | |
| [data-tip]:hover::after, | |
| [data-tip]:focus::after { | |
| opacity: 1; | |
| transform: translateY(0); | |
| } | |
| [data-tip]:focus { | |
| outline: none; | |
| } | |
| [data-tip]:not([data-pos])::before, | |
| [data-tip][data-pos="top"]::before { | |
| bottom: calc(100% + 10px); | |
| left: 50%; | |
| transform: translateX(-50%) translateY(4px); | |
| } | |
| [data-tip]:not([data-pos]):hover::before, | |
| [data-tip]:not([data-pos]):focus::before, | |
| [data-tip][data-pos="top"]:hover::before, | |
| [data-tip][data-pos="top"]:focus::before { | |
| transform: translateX(-50%) translateY(0); | |
| } | |
| [data-tip]:not([data-pos])::after, | |
| [data-tip][data-pos="top"]::after { | |
| bottom: calc(100% + 4px); | |
| left: 50%; | |
| transform: translateX(-50%) translateY(4px); | |
| border-left: 6px solid transparent; | |
| border-right: 6px solid transparent; | |
| border-top: 6px solid var(--tip-border); | |
| } | |
| [data-tip]:not([data-pos]):hover::after, | |
| [data-tip]:not([data-pos]):focus::after, | |
| [data-tip][data-pos="top"]:hover::after, | |
| [data-tip][data-pos="top"]:focus::after { | |
| transform: translateX(-50%) translateY(0); | |
| } | |
| [data-tip][data-pos="bottom"]::before { | |
| top: calc(100% + 10px); | |
| left: 50%; | |
| transform: translateX(-50%) translateY(-4px); | |
| } | |
| [data-tip][data-pos="bottom"]:hover::before, | |
| [data-tip][data-pos="bottom"]:focus::before { | |
| transform: translateX(-50%) translateY(0); | |
| } | |
| [data-tip][data-pos="bottom"]::after { | |
| top: calc(100% + 4px); | |
| left: 50%; | |
| transform: translateX(-50%) translateY(-4px); | |
| border-left: 6px solid transparent; | |
| border-right: 6px solid transparent; | |
| border-bottom: 6px solid var(--tip-border); | |
| } | |
| [data-tip][data-pos="bottom"]:hover::after, | |
| [data-tip][data-pos="bottom"]:focus::after { | |
| transform: translateX(-50%) translateY(0); | |
| } | |
| [data-tip][data-pos="left"]::before { | |
| right: calc(100% + 10px); | |
| top: 50%; | |
| transform: translateY(-50%) translateX(4px); | |
| } | |
| [data-tip][data-pos="left"]:hover::before, | |
| [data-tip][data-pos="left"]:focus::before { | |
| transform: translateY(-50%) translateX(0); | |
| } | |
| [data-tip][data-pos="left"]::after { | |
| right: calc(100% + 4px); | |
| top: 50%; | |
| transform: translateY(-50%) translateX(4px); | |
| border-top: 6px solid transparent; | |
| border-bottom: 6px solid transparent; | |
| border-left: 6px solid var(--tip-border); | |
| } | |
| [data-tip][data-pos="left"]:hover::after, | |
| [data-tip][data-pos="left"]:focus::after { | |
| transform: translateY(-50%) translateX(0); | |
| } | |
| [data-tip][data-pos="right"]::before { | |
| left: calc(100% + 10px); | |
| top: 50%; | |
| transform: translateY(-50%) translateX(-4px); | |
| } | |
| [data-tip][data-pos="right"]:hover::before, | |
| [data-tip][data-pos="right"]:focus::before { | |
| transform: translateY(-50%) translateX(0); | |
| } | |
| [data-tip][data-pos="right"]::after { | |
| left: calc(100% + 4px); | |
| top: 50%; | |
| transform: translateY(-50%) translateX(-4px); | |
| border-top: 6px solid transparent; | |
| border-bottom: 6px solid transparent; | |
| border-right: 6px solid var(--tip-border); | |
| } | |
| [data-tip][data-pos="right"]:hover::after, | |
| [data-tip][data-pos="right"]:focus::after { | |
| transform: translateY(-50%) translateX(0); | |
| } | |
| .btn { | |
| padding: 10px 22px; | |
| border-radius: var(--radius); | |
| font-family: 'DM Sans', sans-serif; | |
| font-size: 0.85rem; | |
| font-weight: 500; | |
| border: 1px solid var(--surface); | |
| background: var(--surface); | |
| color: var(--text); | |
| cursor: pointer; | |
| transition: border-color 0.15s, background 0.15s; | |
| } | |
| .btn:hover { | |
| border-color: var(--muted); | |
| color: var(--surface); | |
| background: var(--muted); | |
| border-radius: var(--radius1); | |
| } | |
| .icon-btn { | |
| width: 50px; | |
| height: 45px; | |
| border-radius: var(--radius); | |
| border: 1px solid var(--surface); | |
| background: var(--surface); | |
| display: inline-flex; | |
| align-items: center; | |
| justify-content: center; | |
| font-size: 1.5rem; | |
| cursor: pointer; | |
| transition: border-color 0.15s, background 0.15s; | |
| } | |
| .icon-btn:hover { | |
| border-color: var(--muted); | |
| color: var(--surface); | |
| background: var(--muted); | |
| border-radius: var(--radius1); | |
| } | |
| [data-tip][data-variant="accent"]::before { | |
| background: var(--accent); | |
| color: #0d0d0d; | |
| border-color: var(--accent); | |
| font-weight: 500; | |
| } | |
| [data-tip][data-variant="accent"]::after { | |
| border-top-color: var(--accent) !important; | |
| } | |
| [data-tip][data-variant="danger"]::before { | |
| background: #ff4444; | |
| color: #fff; | |
| border-color: #ff4444; | |
| } | |
| [data-tip][data-variant="danger"]::after { | |
| border-top-color: #ff4444 !important; | |
| } | |
| [data-tip][data-variant="info"]::before { | |
| background: #2563eb; | |
| color: #fff; | |
| border-color: #2563eb; | |
| } | |
| [data-tip][data-variant="info"]::after { | |
| border-top-color: #2563eb !important; | |
| } | |
| .prose { | |
| font-size: 1rem; | |
| font-weight: 100; | |
| line-height: 1.9; | |
| color: var(--muted); | |
| max-width: 100%; | |
| } | |
| .prose [data-tip] { | |
| color: var(--text); | |
| border-bottom: 1px dashed var(--muted); | |
| cursor: help; | |
| display: inline; | |
| } | |
| .prose [data-tip]::before { | |
| white-space: normal; | |
| width: 200px; | |
| text-align: left; | |
| line-height: 1.5; | |
| } | |
| .note { | |
| font-family: 'DM Mono', monospace; | |
| font-size: 0.65rem; | |
| color: var(--muted); | |
| font-weight: 100; | |
| text-align: center; | |
| letter-spacing: 0.05em; | |
| } | |
| .note span { | |
| color: var(--accent); | |
| } |
| <link href="https://fonts.googleapis.com/css2?family=Honk:MORF@15&display=swap" rel="stylesheet" /> |