HTML
CSS
JS
Result
Skip Results Iframe
EDIT ON
Live
Live
Live
This Pen is owned by
web.dev
on
CodePen
.
See more by @web-dot-dev on CodePen
External CSS
https://codepen.io/web-dot-dev/pen/XWqWYgB.css
https://codepen.io/web-dot-dev/pen/ZExZWBQ.css
External JavaScript
https://codepen.io/web-dot-dev/pen/XWqWYgB.js
https://codepen.io/web-dot-dev/pen/ZExZWBQ.js
{"__browser":{"country":"US","device":"unknown_device","mobile":false,"name":"unknown browser","platform":"unknown_platform","version":"0"},"__constants":{},"__CPDATA":{"domain_iframe":"https://cdpn.io","environment":"production","host":"codepen.io","iframe_allow":"accelerometer *; ambient-light-sensor *; camera *; display-capture *; encrypted-media *; geolocation *; gyroscope *; microphone *; midi *; payment *; vr *; web-share *; serial *; xr-spatial-tracking *","iframe_sandbox":"allow-downloads allow-forms allow-modals allow-pointer-lock allow-popups-to-escape-sandbox allow-popups allow-presentation allow-same-origin allow-scripts allow-top-navigation-by-user-activation"},"__graphql":{"data":{"errors":[{"message":"Cannot return null for non-nullable field Query.sessionUser"}],"data":null},"url":"https://codepen.io/graphql","api":"cprails"},"__pay_stripe_public_key":"pk_live_2GndomDfiklqpSNQn8FrGuwZSMIMzha7DkLJqlYe7IR0ihKAlKdiHg68JJc5eVPt68rzAjzAAVXcUwjySHRCsgjQQ00gtRBUFNH","__pay_braintree_env":"production","__boomboom":{"serve_url":"https://cdpn.io/cpe/boomboom","store_url":"https://codepen.io/cpe/boomboom/store"},"__pageType":"embed","__item":"{\"editor_settings\":{\"auto_run\":true,\"autocomplete\":false,\"code_folding\":true,\"css_pre_processor\":\"none\",\"css_prefix\":\"neither\",\"css_starter\":\"neither\",\"emmet_active\":true,\"font_size\":14,\"font_type\":\"system\",\"format_on_save\":true,\"html_pre_processor\":\"none\",\"indent_with\":\"spaces\",\"js_pre_processor\":\"none\",\"key_bindings\":\"normal\",\"line_numbers\":true,\"line_wrapping\":true,\"match_brackets\":true,\"snippets\":{\"markupSnippets\":{},\"stylesheetSnippets\":{}},\"tab_size\":2,\"theme\":\"twilight\",\"id\":\"jOxENww\",\"auto_save\":true},\"hashid\":\"jOxENww\",\"itemType\":\"pen\",\"resources\":[{\"url\":\"https://codepen.io/web-dot-dev/pen/XWqWYgB.css\",\"order\":0,\"resource_type\":\"css\",\"par_content\":\"\"},{\"url\":\"https://codepen.io/web-dot-dev/pen/XWqWYgB.js\",\"order\":0,\"resource_type\":\"js\",\"par_content\":\"\"},{\"url\":\"https://codepen.io/web-dot-dev/pen/ZExZWBQ.css\",\"order\":1,\"resource_type\":\"css\",\"par_content\":\"\"},{\"url\":\"https://codepen.io/web-dot-dev/pen/ZExZWBQ.js\",\"order\":1,\"resource_type\":\"js\",\"par_content\":\"\"}],\"tags\":[],\"id\":62926129,\"user_id\":5928893,\"html\":\"<div id=\\\"spotlight\\\" popover>\\n <input autocomplete=\\\"off\\\" role=\\\"combobox\\\" spellcheck=\\\"false\\\" aria-expanded=\\\"false\\\" aria-controls=\\\"spotlight-options\\\" aria-activedescendant=\\\"\\\" autofocus id=\\\"spotlight-search\\\" type=\\\"text\\\" placeholder=\\\"Pop-up search...\\\">\\n <input aria-hidden=\\\"true\\\" type=\\\"text\\\" disabled readonly class=\\\"spotlight-under-text\\\">\\n <div popover id=\\\"spotlight-options\\\" role=\\\"listbox\\\">\\n <!-- \\\"Options\\\" get injected here -->\\n </div>\\n</div>\\n<h1>\\n <span>Open this pop-up with</span>\\n <kbd>cmd + j</kbd>\\n</h1>\\n<sub>Note:: You'll need to view this outside of an iframe</sub>\",\"css\":\"@layer demo {\\n :root {\\n --spotlight-bg: hsl(0 0% 90% / 0.9);\\n --spotlight-hover: var(--blue-2);\\n }\\n\\n @media (prefers-color-scheme: dark) {\\n :root {\\n --spotlight-bg: hsl(0 0% 10% / 0.9);\\n --spotlight-hover: var(--blue-8);\\n }\\n }\\n\\n *,\\n *:after,\\n *:before {\\n box-sizing: border-box;\\n }\\n\\n body {\\n min-height: 100vh;\\n display: grid;\\n place-items: center;\\n align-content: center;\\n gap: var(--size-8);\\n font-family: \\\"Google Sans\\\", sans-serif, system-ui;\\n }\\n\\n :where([popover]) {\\n margin: auto;\\n border-width: initial;\\n border-style: solid;\\n }\\n\\n h1 {\\n display: flex;\\n max-inline-size: 100%;\\n flex-wrap: wrap;\\n gap: 1ch;\\n align-items: center;\\n justify-content: center;\\n color: var(--text-2);\\n line-height: 0.8;\\n }\\n\\n kbd {\\n color: var(--blue-6);\\n background: var(--surface-1);\\n }\\n\\n #spotlight,\\n #spotlight-options {\\n border: 0;\\n padding: 0;\\n width: var(--size-content-3);\\n max-width: calc(100% - (2 * var(--size-4)));\\n background: var(--spotlight-bg);\\n backdrop-filter: blur(4px);\\n overflow: hidden;\\n }\\n\\n #spotlight {\\n top: 25%;\\n margin: 0;\\n left: 50%;\\n transform: translateX(-50%);\\n border-radius: var(--radius-3);\\n box-shadow: var(--shadow-5);\\n }\\n\\n #spotlight-search::placeholder {\\n color: var(--text-2);\\n }\\n\\n #spotlight:has(#spotlight-options:open) {\\n border-bottom-left-radius: 0;\\n border-bottom-right-radius: 0;\\n }\\n\\n #spotlight-options {\\n box-shadow: var(--shadow-5);\\n border-top: 1px solid var(--gray-5);\\n border-bottom-left-radius: var(--radius-3);\\n border-bottom-right-radius: var(--radius-3);\\n }\\n\\n [data-theme=\\\"dark\\\"] {\\n color-scheme: dark;\\n --brand: var(--pink-4);\\n --link: var(--indigo-3);\\n --link-visited: var(--grape-3);\\n --text-1: var(--gray-1);\\n --text-2: var(--gray-2);\\n --surface-1: var(--gray-9);\\n --surface-2: var(--gray-8);\\n --surface-3: var(--gray-7);\\n --surface-4: var(--gray-6);\\n --nav-icon: var(--gray-5);\\n --nav-icon-hover: var(--gray-2);\\n --shadow-strength: 10%;\\n --shadow-color: 220 40% 2%;\\n --spotlight-bg: hsl(0 0% 10% / 0.9);\\n --spotlight-hover: var(--blue-8);\\n }\\n\\n [data-theme=\\\"light\\\"] {\\n color-scheme: light;\\n --brand: var(--pink-6);\\n --link: var(--indigo-7);\\n --link-visited: var(--grape-7);\\n --text-1: var(--gray-9);\\n --text-2: var(--gray-7);\\n --surface-1: var(--gray-0);\\n --surface-2: var(--gray-2);\\n --surface-3: var(--gray-3);\\n --surface-4: var(--gray-4);\\n --nav-icon: var(--gray-7);\\n --nav-icon-hover: var(--gray-9);\\n --shadow-color: 220 3% 15%;\\n --shadow-strength: 1%;\\n --spotlight-bg: hsl(0 0% 90% / 0.9);\\n --spotlight-hover: var(--blue-2);\\n }\\n\\n [data-option] {\\n background: transparent;\\n padding: var(--size-4);\\n height: 100%;\\n width: 100%;\\n display: flex;\\n justify-content: flex-start;\\n text-align: left;\\n align-items: center;\\n gap: 1ch;\\n padding-left: var(--size-8);\\n cursor: pointer;\\n color: var(--text-1);\\n }\\n\\n [data-option][aria-selected=\\\"true\\\"] {\\n background: var(--spotlight-hover);\\n }\\n\\n #spotlight-search,\\n [readonly] {\\n padding: var(--size-4);\\n width: 100%;\\n background: transparent;\\n padding-left: var(--size-8);\\n outline: transparent;\\n color: var(--text-1);\\n }\\n\\n [readonly] {\\n position: absolute;\\n inset: 0;\\n z-index: -1;\\n }\\n #spotlight-options {\\n margin: 0;\\n top: calc(var(--top, 0) * 1px);\\n left: calc(var(--left, 0) * 1px);\\n }\\n}\\n\\n@layer base {\\n :root {\\n --spotlight-bg: hsl(0 0% 90% / 0.9);\\n --spotlight-hover: var(--blue-2);\\n }\\n\\n @media (prefers-color-scheme: dark) {\\n :root {\\n --spotlight-bg: hsl(0 0% 10% / 0.9);\\n --spotlight-hover: var(--blue-8);\\n }\\n }\\n\\n *,\\n *:after,\\n *:before {\\n box-sizing: border-box;\\n }\\n\\n body {\\n min-height: 100vh;\\n display: grid;\\n place-items: center;\\n font-family: \\\"Google Sans\\\", sans-serif, system-ui;\\n }\\n\\n :where([popover]) {\\n margin: auto;\\n border-width: initial;\\n border-style: solid;\\n }\\n\\n h1 {\\n display: flex;\\n max-inline-size: 100%;\\n flex-wrap: wrap;\\n gap: 1ch;\\n align-items: center;\\n justify-content: center;\\n color: var(--text-2);\\n line-height: 0.8;\\n }\\n\\n kbd {\\n color: var(--blue-6);\\n background: var(--surface-1);\\n }\\n}\\n\",\"js\":\"const POPUP = document.querySelector(\\\"#spotlight\\\");\\n\\n// Keys are 91 && 74.\\nconst CMD = 91;\\nconst MOD = 74;\\n\\nconst STATE = {\\n cmd: false,\\n mod: false\\n};\\n\\nconst handleActivation = (e) => {\\n if (e.keyCode === CMD && !STATE.cmd) STATE.cmd = true;\\n if (e.keyCode === MOD && STATE.cmd && !STATE.mod) STATE.mod = true;\\n\\n if (STATE.cmd && STATE.mod && !POPUP.matches(\\\":open\\\")) {\\n STATE.cmd = STATE.mod = false;\\n POPUP.showPopover();\\n\\n OPTIONS.showPopover();\\n }\\n};\\n\\nconst unload = (e) => {\\n if (e.keyCode === CMD || e.keyCode === MOD) STATE.cmd = STATE.mod = false;\\n};\\n\\ndocument.body.addEventListener(\\\"keydown\\\", handleActivation);\\ndocument.body.addEventListener(\\\"keyup\\\", unload);\\n\\ndocument.documentElement.setAttribute(\\n \\\"data-theme\\\",\\n window.matchMedia(\\\"(prefers-color-scheme: dark)\\\").matches ? \\\"dark\\\" : \\\"light\\\"\\n);\\n\\nlet length = 0;\\nconst OPTIONS = document.querySelector(\\\"#spotlight-options\\\");\\nconst SEARCH = document.querySelector(\\\"#spotlight-search\\\");\\nconst AVAILABLE_OPTIONS = [\\n {\\n id: \\\"toggle-theme\\\",\\n title: 'Toggle theme <i class=\\\"material-symbols-outlined\\\">routine</i>',\\n action: () => {\\n document.documentElement.setAttribute(\\n \\\"data-theme\\\",\\n document.documentElement.matches('[data-theme=\\\"dark\\\"]')\\n ? \\\"light\\\"\\n : \\\"dark\\\"\\n );\\n }\\n },\\n {\\n id: \\\"twitter-reach\\\",\\n title:\\n 'Say \\\"Hey\\\" on Twitter <i class=\\\"material-symbols-outlined\\\">waving_hand</i>',\\n action: () => {\\n window.open(\\\"https://twitter.com/jh3yy\\\", \\\"_blank\\\");\\n }\\n },\\n {\\n id: \\\"popup-explainer\\\",\\n title:\\n 'Check out the Pop-up API Explainer <i class=\\\"material-symbols-outlined\\\">mode_comment</i>',\\n action: () => {\\n window.open(\\n \\\"https://open-ui.org/components/popup.research.explainer\\\",\\n \\\"_blank\\\"\\n );\\n }\\n },\\n {\\n id: \\\"web-search\\\",\\n title: (value) =>\\n `Search web for \\\"${value}\\\" <i class=\\\"material-symbols-outlined\\\">public</i>`,\\n action: (value) => {\\n window.open(`https://google.com/search?q=${value}`, \\\"_blank\\\");\\n }\\n }\\n];\\n\\nconst fireAction = (actionId) => {\\n AVAILABLE_OPTIONS.filter((option) => option.id === actionId)[0].action(\\n SEARCH.value\\n );\\n if (actionId !== \\\"toggle-theme\\\") {\\n SEARCH.value = \\\"\\\";\\n POPUP.hidePopover();\\n }\\n};\\n\\nconst buildOptions = (options, value) => {\\n let items = \\\"\\\";\\n options.forEach((option, index) => {\\n if (option)\\n items += `\\n <div id=\\\"${index}\\\" role=\\\"option\\\" aria-selected=\\\"${\\n index === 0\\n }\\\" data-option=\\\"${option.id}\\\">\\n ${typeof option.title === \\\"string\\\" ? option.title : option.title(value)}\\n </div>\\n `;\\n });\\n OPTIONS.innerHTML = items;\\n};\\n\\nconst onOptionsOpen = () => {\\n const { bottom, left } = POPUP.getBoundingClientRect();\\n SEARCH.setAttribute(\\\"aria-expanded\\\", true);\\n OPTIONS.style.setProperty(\\\"--top\\\", bottom);\\n OPTIONS.style.setProperty(\\\"--left\\\", left);\\n};\\n\\nconst onOptionsHide = (e) => {\\n SEARCH.setAttribute(\\\"aria-expanded\\\", false);\\n};\\n\\nconst onActivated = (e) => {\\n if (e.target === POPUP) {\\n renderOptions(SEARCH.value);\\n }\\n};\\n\\nconst selectOption = (e) => {\\n let selected;\\n if (e.type === \\\"pointermove\\\") {\\n const opts = [...OPTIONS.children];\\n opts.forEach((option) => {\\n let target = e.target.tagName === \\\"I\\\" ? e.target.parentNode : e.target;\\n option.setAttribute(\\\"aria-selected\\\", option === target ? true : false);\\n if (option === target) selected = option;\\n });\\n } else {\\n e.preventDefault();\\n const CURRENT = document.querySelector('[aria-selected=\\\"true\\\"]');\\n selected =\\n CURRENT[\\n e.keyCode === 38 ? \\\"previousElementSibling\\\" : \\\"nextElementSibling\\\"\\n ];\\n if (!selected)\\n selected =\\n CURRENT.parentNode[\\n e.keyCode === 38 ? \\\"lastElementChild\\\" : \\\"firstElementChild\\\"\\n ];\\n CURRENT.setAttribute(\\\"aria-selected\\\", false);\\n selected.setAttribute(\\\"aria-selected\\\", true);\\n }\\n if (selected)\\n SEARCH.setAttribute(\\n \\\"aria-activedescendant\\\",\\n selected.getAttribute(\\\"data-option\\\")\\n );\\n};\\n\\nconst renderOptions = (value) => {\\n length = value.length;\\n let options = [\\n ...AVAILABLE_OPTIONS.filter((option) => {\\n return (\\n typeof option.title === \\\"string\\\" &&\\n option.title.toLowerCase().indexOf(value.toLowerCase()) !== -1\\n );\\n }),\\n value !== \\\"\\\"\\n ? AVAILABLE_OPTIONS.filter((option) => option.id === \\\"web-search\\\")[0]\\n : null\\n ];\\n // Regardless of what's happening, you're building the options...\\n buildOptions(options, value);\\n\\n if (!OPTIONS.matches(\\\":open\\\") && options.length) {\\n OPTIONS.showPopover();\\n }\\n};\\n\\nconst handleActionClick = (e) => {\\n fireAction(\\n e.target.tagName === \\\"I\\\"\\n ? e.target.parentNode.getAttribute(\\\"data-option\\\")\\n : e.target.getAttribute(\\\"data-option\\\")\\n );\\n};\\n\\nconst handleKeyboardInteraction = (e) => {\\n // Handle building of options\\n if (POPUP.matches(\\\":open\\\")) {\\n if (e.keyCode === 13 && e.type === \\\"keydown\\\") {\\n e.preventDefault();\\n fireAction(\\n document\\n .querySelector('[aria-selected=\\\"true\\\"]')\\n .getAttribute(\\\"data-option\\\")\\n );\\n } else if (SEARCH.value.length !== length) {\\n renderOptions(SEARCH.value);\\n } else if (\\n OPTIONS.matches(\\\":open\\\") &&\\n (e.keyCode === 38 || e.keyCode === 40) &&\\n e.type === \\\"keydown\\\"\\n ) {\\n selectOption(e);\\n } else if (e.keyCode === 27) {\\n SEARCH.value = \\\"\\\";\\n }\\n }\\n};\\n\\nPOPUP.addEventListener(\\\"beforetoggle\\\", (e) => {\\n if (e.newState === \\\"open\\\") onActivated(e);\\n});\\nwindow.addEventListener(\\\"keydown\\\", handleKeyboardInteraction);\\nwindow.addEventListener(\\\"keypress\\\", handleKeyboardInteraction);\\nwindow.addEventListener(\\\"keyup\\\", handleKeyboardInteraction);\\nOPTIONS.addEventListener(\\\"beforetoggle\\\", (e) => {\\n if (e.newState === \\\"open\\\") onOptionsOpen(e);\\n else onOptionsHide(e);\\n});\\nOPTIONS.addEventListener(\\\"pointermove\\\", selectOption);\\nOPTIONS.addEventListener(\\\"click\\\", handleActionClick);\\n\\n// Catch case for clicking on the input\\nSEARCH.addEventListener(\\\"click\\\", (e) => {\\n // Don't want the click on the input to close the popup\\n OPTIONS.showPopover();\\n});\\n\",\"html_pre_processor\":\"none\",\"css_pre_processor\":\"none\",\"js_pre_processor\":\"none\",\"html_classes\":\"popup-support\",\"css_starter\":\"neither\",\"js_library\":null,\"created_at\":\"2022-09-03T20:18:26.583Z\",\"updated_at\":\"2023-01-19T11:23:16.063Z\",\"title\":\"22. Spotlight search pop-up\",\"description\":\"\",\"slug_hash\":\"jOxENww\",\"head\":\"\",\"private\":false,\"has_animation\":false,\"team_id\":0,\"css_prefix\":\"neither\",\"template\":false,\"parent_id\":0,\"comments_count\":0,\"custom_screenshot_filename\":null,\"loves_count\":0,\"pick\":false,\"popularity_score\":0,\"views_count\":0,\"pick_visible_at\":null,\"cpid\":\"01830501-2c97-766b-a282-74e285d2a753\",\"is_new_editor_pen\":false,\"protected\":false,\"access\":\"Public\",\"pen_hash\":null}","__processorsMap":{"autoprefixer":"autoprefixer-10","babel":"babel-7","coffeescript":"coffeescript-2","format-1":"format-1","flutter":"flutter-1","haml":"haml-4","less":"less-3","lint-1":"lint-1","livescript":"livescript-1","markdown":"markdown-11","postcss":"postcss-7","pug":"pug-2","sass":"sass-1","scss":"sass-1","sass-ruby-3":"sass-ruby-3","sass-ruby-compass-3":"sass-ruby-compass-3","slim":"slim-3","stylus":"stylus-0","typescript":"typescript-4","vue":"vue-2","vue3":"vue-3"},"__favicon_mask_icon":"https://cpwebassets.codepen.io/assets/favicon/logo-pin-b4b4269c16397ad2f0f7a01bcdf513a1994f4c94b8af2f191c09eb0d601762b1.svg","__favicon_shortcut_icon":"https://cpwebassets.codepen.io/assets/favicon/favicon-aec34940fbc1a6e787974dcd360f2c6b63348d4b1f4e06c77743096d55480f33.ico","__path_to_iframe_console_runner":"https://cpwebassets.codepen.io/assets/editor/iframe/iframeConsoleRunner-6d8bf8b4b479137260842506acbb12717dace0823c023e08b96360e60b0840d9.js","__path_to_iframe_refresh_css":"https://cpwebassets.codepen.io/assets/editor/iframe/iframeRefreshCSS-44fe83e49b63affec96918c9af88c0d80b209a862cf87ac46bc933074b8c557d.js","__path_to_iframe_runtime_errors":"https://cpwebassets.codepen.io/assets/editor/iframe/iframeRuntimeErrors-4f205f2c14e769b448bcf477de2938c681660d5038bc464e3700256713ebe261.js","__path_to_processor_worker":"https://cpwebassets.codepen.io/assets/packs/router.js","__path_to_stop_execution_on_timeout":"https://cpwebassets.codepen.io/assets/common/stopExecutionOnTimeout-2c7831bb44f98c1391d6a4ffda0e1fd302503391ca806e7fcc7b9b87197aec26.js","__pen_normalize_css_url":"https://cdnjs.cloudflare.com/ajax/libs/normalize/5.0.0/normalize.min.css","__pen_prefix_free_url":"https://cdnjs.cloudflare.com/ajax/libs/prefixfree/1.0.7/prefixfree.min.js","__pen_reset_css_url":"https://cdnjs.cloudflare.com/ajax/libs/meyer-reset/2.0/reset.min.css"}