A Pen by mike-at-redspace on CodePen.
Created
April 17, 2026 07:57
-
-
Save mike-at-redspace/9c083d24722b58e0b58621d7d7df143c to your computer and use it in GitHub Desktop.
Nerd Fonts (virtualized)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| h1 Nerd Fonts | |
| #toast | |
| .toolbar | |
| input#search(type='text' placeholder='Search glyphs…' autocomplete='off' spellcheck='false') | |
| span#count | |
| #viewport | |
| div | |
| #spacer | |
| #grid |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| import * as nf from "https://esm.sh/@m234/nerd-fonts"; | |
| const $ = (id) => document.getElementById(id); | |
| const [search, count, viewport, spacer, grid, toast] = [ | |
| "search", | |
| "count", | |
| "viewport", | |
| "spacer", | |
| "grid", | |
| "toast" | |
| ].map($); | |
| const ROW_H = 94, | |
| COL_W = 96, | |
| OVERSCAN = 3; | |
| let filtered, | |
| cols = 1, | |
| rafId, | |
| toastTimer, | |
| searchTimer, | |
| isFiltering = false; | |
| const all = Object.entries(nf.icons).map(([name, { value }]) => ({ | |
| name, | |
| short: name.replace(/^nf-[^-]+-/, ""), | |
| value, | |
| cp: value.codePointAt(0).toString(16).toUpperCase().padStart(4, "0") | |
| })); | |
| filtered = all; | |
| const showToast = (msg) => { | |
| toast.textContent = msg; | |
| toast.className = "show"; | |
| clearTimeout(toastTimer); | |
| toastTimer = setTimeout(() => (toast.className = ""), 1800); | |
| }; | |
| const render = () => { | |
| cols = Math.max(1, Math.floor((viewport.clientWidth + 2) / COL_W)); | |
| grid.style.setProperty("--cols", cols); | |
| const rows = Math.ceil(filtered.length / cols); | |
| spacer.style.height = `${rows * ROW_H}px`; | |
| const { scrollTop, clientHeight } = viewport; | |
| const first = Math.max(0, Math.floor(scrollTop / ROW_H) - OVERSCAN); | |
| const last = Math.min( | |
| rows - 1, | |
| Math.ceil((scrollTop + clientHeight) / ROW_H) + OVERSCAN | |
| ); | |
| grid.style.top = `${first * ROW_H}px`; | |
| const start = first * cols; | |
| const frag = document.createDocumentFragment(); | |
| filtered.slice(start, (last + 1) * cols).forEach((g, i) => { | |
| const el = document.createElement("div"); | |
| el.className = `cell ${isFiltering ? "entering" : ""}`; | |
| el.title = el.dataset.name = g.name; | |
| el.dataset.value = g.value; | |
| if (isFiltering) { | |
| const rowOffset = Math.floor(i / cols); | |
| const colIndex = i % cols; | |
| // • 35ms per row → nice downward cascade | |
| // • 12ms per col → subtle left-to-right wave inside each row | |
| const delayMs = Math.round(rowOffset * 35 + colIndex * 12); | |
| el.style.setProperty("--enter-delay", `${delayMs}ms`); | |
| el.addEventListener( | |
| "animationend", | |
| () => el.classList.remove("entering"), | |
| { once: true } | |
| ); | |
| } | |
| el.innerHTML = ` | |
| <span class="glyph">${g.value}</span> | |
| <span class="name">${g.short}</span> | |
| <span class="cp">U+${g.cp}</span>`; | |
| frag.append(el); | |
| }); | |
| grid.replaceChildren(frag); | |
| count.textContent = `${filtered.length.toLocaleString()} glyphs`; | |
| }; | |
| const schedule = () => { | |
| cancelAnimationFrame(rafId); | |
| rafId = requestAnimationFrame(render); | |
| }; | |
| search.addEventListener("input", (e) => { | |
| clearTimeout(searchTimer); | |
| searchTimer = setTimeout(() => { | |
| const q = e.target.value.trim().toLowerCase(); | |
| filtered = q | |
| ? all.filter((g) => g.name.includes(q) || g.cp.toLowerCase().includes(q)) | |
| : all; | |
| viewport.scrollTop = 0; | |
| isFiltering = true; | |
| render(); | |
| isFiltering = false; | |
| }, 120); | |
| }); | |
| viewport.addEventListener("scroll", schedule, { passive: true }); | |
| window.addEventListener("resize", schedule); | |
| grid.addEventListener("click", (e) => { | |
| const c = e.target.closest(".cell"); | |
| if (!c) return; | |
| navigator.clipboard.writeText(c.dataset.name); | |
| c.classList.add("copied"); | |
| setTimeout(() => c.classList.remove("copied"), 700); | |
| showToast(`Copied ${c.dataset.name}`); | |
| }); | |
| grid.addEventListener("contextmenu", (e) => { | |
| const c = e.target.closest(".cell"); | |
| if (!c) return; | |
| e.preventDefault(); | |
| navigator.clipboard.writeText(c.dataset.value); | |
| showToast(`Copied glyph ${c.dataset.value}`); | |
| }); | |
| render(); |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| :root { | |
| --grad-start: oklch(58% 0.28 255); | |
| --grad-end: oklch(42% 0.34 295); | |
| --grad-angle: 110deg; | |
| --surface: oklch(100% 0 0 / 0.08); | |
| --surface-hover: oklch(100% 0 0 / 0.25); | |
| --surface-active: oklch(100% 0 0 / 0.35); | |
| --border: oklch(100% 0 0 / 0.14); | |
| --border-hover: oklch(100% 0 0 / 0.5); | |
| --border-focus: oklch(100% 0 0 / 0.7); | |
| --text-primary: oklch(100% 0 0); | |
| --text-muted: oklch(100% 0 0 / 0.65); | |
| --text-faint: oklch(100% 0 0 / 0.35); | |
| --text-placeholder: oklch(100% 0 0 / 0.45); | |
| --copied-bg: oklch(75% 0.18 145 / 0.25); | |
| --copied-border: oklch(75% 0.18 145 / 0.6); | |
| --scrollbar-thumb: oklch(100% 0 0 / 0.25); | |
| --scrollbar-hover: oklch(100% 0 0 / 0.45); | |
| --cell-h: 88px; | |
| --cell-min-w: 90px; | |
| --gap: 6px; | |
| --radius: 8px; | |
| --radius-pill: 20px; | |
| --pad-page: 1.5rem; | |
| --scrollbar-w: 4px; | |
| --font-ui: system-ui, sans-serif; | |
| --font-mono: monospace; | |
| --font-nerd: "Symbols Nerd Font", monospace; | |
| } | |
| /* ─── font face ─────────────────────────────────────────────────── */ | |
| @font-face { | |
| font-family: "Symbols Nerd Font"; | |
| src: url("https://github.com/ryanoasis/nerd-fonts/raw/HEAD/patched-fonts/NerdFontsSymbolsOnly/SymbolsNerdFont-Regular.ttf") format("truetype"); | |
| } | |
| *, *::before, *::after { | |
| box-sizing: border-box; | |
| margin: 0; | |
| padding: 0; | |
| } | |
| html, body { | |
| height: 100%; | |
| overflow: hidden; | |
| } | |
| body { | |
| font-family: var(--font-ui); | |
| display: flex; | |
| flex-direction: column; | |
| height: 100dvh; | |
| background: linear-gradient(var(--grad-angle), var(--grad-start) 0%, var(--grad-end) 100%); | |
| color: var(--text-primary); | |
| padding: var(--pad-page) var(--scrollbar-w) var(--pad-page) var(--pad-page); | |
| } | |
| h1 { | |
| margin-bottom: 0.5rem; | |
| } | |
| .toolbar { | |
| display: flex; | |
| align-items: center; | |
| gap: 12px; | |
| margin-bottom: 1rem; | |
| flex-shrink: 0; | |
| } | |
| #search { | |
| flex: 1; | |
| max-width: 360px; | |
| padding: 8px 14px; | |
| font-size: 14px; | |
| background: var(--surface); | |
| border: 1px solid var(--border); | |
| border-radius: var(--radius); | |
| color: var(--text-primary); | |
| outline: none; | |
| transition: border-color 0.15s, background 0.15s; | |
| &::placeholder { color: var(--text-placeholder); } | |
| &:focus { | |
| border-color: var(--border-focus); | |
| background: var(--surface-hover); | |
| } | |
| } | |
| #count { | |
| font-size: 12px; | |
| color: var(--text-faint); | |
| white-space: nowrap; | |
| } | |
| // virtualised viewport | |
| #viewport { | |
| flex: 1; | |
| overflow-y: scroll; | |
| overflow-x: hidden; | |
| position: relative; | |
| scrollbar-width: thin; | |
| scrollbar-color: var(--scrollbar-thumb) transparent; | |
| padding-top: 2px; | |
| &::-webkit-scrollbar { width: var(--scrollbar-w); } | |
| &::-webkit-scrollbar-track { background: transparent; } | |
| &::-webkit-scrollbar-thumb { | |
| background: var(--scrollbar-thumb); | |
| border-radius: calc(var(--scrollbar-w) / 2); | |
| &:hover { background: var(--scrollbar-hover); } | |
| } | |
| > * { | |
| position: relative; | |
| } | |
| } | |
| #spacer { width: 100%; } | |
| #grid { | |
| display: grid; | |
| grid-template-columns: repeat(var(--cols), 1fr); | |
| gap: var(--gap); | |
| position: absolute; | |
| left: 0; | |
| right: var(--scrollbar-w); | |
| > * { | |
| min-width: 0; | |
| } | |
| } | |
| .cell { | |
| display: flex; | |
| flex-direction: column; | |
| align-items: center; | |
| gap: 5px; | |
| padding: 12px 6px 9px; | |
| background: var(--surface); | |
| border: 1px solid var(--border); | |
| border-radius: var(--radius); | |
| cursor: pointer; | |
| user-select: none; | |
| transition: background 0.1s, border-color 0.1s, transform 0.1s; | |
| min-width: 0; | |
| &:hover { | |
| background: var(--surface-hover); | |
| border-color: var(--border-hover); | |
| transform: translateY(-1px); | |
| .glyph, .name { | |
| color: oklch(95% 0.28 195); | |
| } | |
| .glyph { | |
| transform: scale(1.2); | |
| } | |
| } | |
| &:active { transform: translateY(0); } | |
| &.copied { | |
| background: var(--copied-bg); | |
| border-color: var(--copied-border); | |
| } | |
| &.entering { | |
| animation: glyph-enter 340ms cubic-bezier(0.4, 0, 0.2, 1) both; | |
| animation-delay: var(--enter-delay, 0ms); | |
| } | |
| .glyph { | |
| font-family: var(--font-nerd); | |
| font-size: clamp(24px, 2.5vw, 36px); | |
| line-height: 1; | |
| color: var(--text-primary); | |
| transition: color .2s ease, transform 0.1s; | |
| } | |
| .name { | |
| font-size: 10px; | |
| color: var(--text-muted); | |
| text-align: center; | |
| line-height: 1.3; | |
| width: 100%; | |
| min-width: 0; | |
| white-space: nowrap; | |
| overflow: hidden; | |
| text-overflow: ellipsis; | |
| transition: color .2s ease; | |
| } | |
| .cp { | |
| font-size: 9px; | |
| color: var(--text-faint); | |
| font-family: var(--font-mono); | |
| } | |
| } | |
| #toast { | |
| position: fixed; | |
| bottom: var(--pad-page); | |
| left: 50%; | |
| translate: -50% 4px; | |
| background: var(--surface-hover); | |
| backdrop-filter: blur(8px); | |
| border: 1px solid var(--border); | |
| border-radius: var(--radius-pill); | |
| padding: 7px 16px; | |
| font-size: 13px; | |
| color: var(--text-primary); | |
| white-space: nowrap; | |
| pointer-events: none; | |
| opacity: 0; | |
| transition: opacity 0.15s, translate 0.15s; | |
| text-shadow: 0 1px 2px oklch(0% 0 0 / 0.3); | |
| z-index: 9999; | |
| &.show { | |
| opacity: 1; | |
| translate: -50% 0; | |
| } | |
| } | |
| @keyframes glyph-enter { | |
| from { | |
| opacity: 0; | |
| transform: scale(0.8) translateY(10px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: scale(1) translateY(0); | |
| } | |
| } | |
| @keyframes glyph-enter { | |
| from { | |
| opacity: 0; | |
| transform: scale(0.85) translateY(14px); | |
| } | |
| to { | |
| opacity: 1; | |
| transform: scale(1) translateY(0); | |
| } | |
| } | |
| @keyframes glyph-exit { | |
| to { | |
| opacity: 0; | |
| transform: scale(0.85) translateY(-10px); | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment