@layer demo {
:root {
--spotlight-bg: hsl(0 0% 90% / 0.9);
--spotlight-hover: var(--blue-2);
}
@media (prefers-color-scheme: dark) {
:root {
--spotlight-bg: hsl(0 0% 10% / 0.9);
--spotlight-hover: var(--blue-8);
}
}
*,
*:after,
*:before {
box-sizing: border-box;
}
body {
min-height: 100vh;
display: grid;
place-items: center;
align-content: center;
gap: var(--size-8);
font-family: "Google Sans", sans-serif, system-ui;
}
:where([popover]) {
margin: auto;
border-width: initial;
border-style: solid;
}
h1 {
display: flex;
max-inline-size: 100%;
flex-wrap: wrap;
gap: 1ch;
align-items: center;
justify-content: center;
color: var(--text-2);
line-height: 0.8;
}
kbd {
color: var(--blue-6);
background: var(--surface-1);
}
#spotlight,
#spotlight-options {
border: 0;
padding: 0;
width: var(--size-content-3);
max-width: calc(100% - (2 * var(--size-4)));
background: var(--spotlight-bg);
backdrop-filter: blur(4px);
overflow: hidden;
}
#spotlight {
top: 25%;
margin: 0;
left: 50%;
transform: translateX(-50%);
border-radius: var(--radius-3);
box-shadow: var(--shadow-5);
}
#spotlight-search::placeholder {
color: var(--text-2);
}
#spotlight:has(#spotlight-options:open) {
border-bottom-left-radius: 0;
border-bottom-right-radius: 0;
}
#spotlight-options {
box-shadow: var(--shadow-5);
border-top: 1px solid var(--gray-5);
border-bottom-left-radius: var(--radius-3);
border-bottom-right-radius: var(--radius-3);
}
[data-theme="dark"] {
color-scheme: dark;
--brand: var(--pink-4);
--link: var(--indigo-3);
--link-visited: var(--grape-3);
--text-1: var(--gray-1);
--text-2: var(--gray-2);
--surface-1: var(--gray-9);
--surface-2: var(--gray-8);
--surface-3: var(--gray-7);
--surface-4: var(--gray-6);
--nav-icon: var(--gray-5);
--nav-icon-hover: var(--gray-2);
--shadow-strength: 10%;
--shadow-color: 220 40% 2%;
--spotlight-bg: hsl(0 0% 10% / 0.9);
--spotlight-hover: var(--blue-8);
}
[data-theme="light"] {
color-scheme: light;
--brand: var(--pink-6);
--link: var(--indigo-7);
--link-visited: var(--grape-7);
--text-1: var(--gray-9);
--text-2: var(--gray-7);
--surface-1: var(--gray-0);
--surface-2: var(--gray-2);
--surface-3: var(--gray-3);
--surface-4: var(--gray-4);
--nav-icon: var(--gray-7);
--nav-icon-hover: var(--gray-9);
--shadow-color: 220 3% 15%;
--shadow-strength: 1%;
--spotlight-bg: hsl(0 0% 90% / 0.9);
--spotlight-hover: var(--blue-2);
}
[data-option] {
background: transparent;
padding: var(--size-4);
height: 100%;
width: 100%;
display: flex;
justify-content: flex-start;
text-align: left;
align-items: center;
gap: 1ch;
padding-left: var(--size-8);
cursor: pointer;
color: var(--text-1);
}
[data-option][aria-selected="true"] {
background: var(--spotlight-hover);
}
#spotlight-search,
[readonly] {
padding: var(--size-4);
width: 100%;
background: transparent;
padding-left: var(--size-8);
outline: transparent;
color: var(--text-1);
}
[readonly] {
position: absolute;
inset: 0;
z-index: -1;
}
#spotlight-options {
margin: 0;
top: calc(var(--top, 0) * 1px);
left: calc(var(--left, 0) * 1px);
}
}
@layer base {
:root {
--spotlight-bg: hsl(0 0% 90% / 0.9);
--spotlight-hover: var(--blue-2);
}
@media (prefers-color-scheme: dark) {
:root {
--spotlight-bg: hsl(0 0% 10% / 0.9);
--spotlight-hover: var(--blue-8);
}
}
*,
*:after,
*:before {
box-sizing: border-box;
}
body {
min-height: 100vh;
display: grid;
place-items: center;
font-family: "Google Sans", sans-serif, system-ui;
}
:where([popover]) {
margin: auto;
border-width: initial;
border-style: solid;
}
h1 {
display: flex;
max-inline-size: 100%;
flex-wrap: wrap;
gap: 1ch;
align-items: center;
justify-content: center;
color: var(--text-2);
line-height: 0.8;
}
kbd {
color: var(--blue-6);
background: var(--surface-1);
}
}
const POPUP = document.querySelector("#spotlight");
// Keys are 91 && 74.
const CMD = 91;
const MOD = 74;
const STATE = {
cmd: false,
mod: false
};
const handleActivation = (e) => {
if (e.keyCode === CMD && !STATE.cmd) STATE.cmd = true;
if (e.keyCode === MOD && STATE.cmd && !STATE.mod) STATE.mod = true;
if (STATE.cmd && STATE.mod && !POPUP.matches(":open")) {
STATE.cmd = STATE.mod = false;
POPUP.showPopover();
OPTIONS.showPopover();
}
};
const unload = (e) => {
if (e.keyCode === CMD || e.keyCode === MOD) STATE.cmd = STATE.mod = false;
};
document.body.addEventListener("keydown", handleActivation);
document.body.addEventListener("keyup", unload);
document.documentElement.setAttribute(
"data-theme",
window.matchMedia("(prefers-color-scheme: dark)").matches ? "dark" : "light"
);
let length = 0;
const OPTIONS = document.querySelector("#spotlight-options");
const SEARCH = document.querySelector("#spotlight-search");
const AVAILABLE_OPTIONS = [
{
id: "toggle-theme",
title: 'Toggle theme <i class="material-symbols-outlined">routine</i>',
action: () => {
document.documentElement.setAttribute(
"data-theme",
document.documentElement.matches('[data-theme="dark"]')
? "light"
: "dark"
);
}
},
{
id: "twitter-reach",
title:
'Say "Hey" on Twitter <i class="material-symbols-outlined">waving_hand</i>',
action: () => {
window.open("https://twitter.com/jh3yy", "_blank");
}
},
{
id: "popup-explainer",
title:
'Check out the Pop-up API Explainer <i class="material-symbols-outlined">mode_comment</i>',
action: () => {
window.open(
"https://open-ui.org/components/popup.research.explainer",
"_blank"
);
}
},
{
id: "web-search",
title: (value) =>
`Search web for "${value}" <i class="material-symbols-outlined">public</i>`,
action: (value) => {
window.open(`https://google.com/search?q=${value}`, "_blank");
}
}
];
const fireAction = (actionId) => {
AVAILABLE_OPTIONS.filter((option) => option.id === actionId)[0].action(
SEARCH.value
);
if (actionId !== "toggle-theme") {
SEARCH.value = "";
POPUP.hidePopover();
}
};
const buildOptions = (options, value) => {
let items = "";
options.forEach((option, index) => {
if (option)
items += `
<div id="${index}" role="option" aria-selected="${
index === 0
}" data-option="${option.id}">
${typeof option.title === "string" ? option.title : option.title(value)}
</div>
`;
});
OPTIONS.innerHTML = items;
};
const onOptionsOpen = () => {
const { bottom, left } = POPUP.getBoundingClientRect();
SEARCH.setAttribute("aria-expanded", true);
OPTIONS.style.setProperty("--top", bottom);
OPTIONS.style.setProperty("--left", left);
};
const onOptionsHide = (e) => {
SEARCH.setAttribute("aria-expanded", false);
};
const onActivated = (e) => {
if (e.target === POPUP) {
renderOptions(SEARCH.value);
}
};
const selectOption = (e) => {
let selected;
if (e.type === "pointermove") {
const opts = [...OPTIONS.children];
opts.forEach((option) => {
let target = e.target.tagName === "I" ? e.target.parentNode : e.target;
option.setAttribute("aria-selected", option === target ? true : false);
if (option === target) selected = option;
});
} else {
e.preventDefault();
const CURRENT = document.querySelector('[aria-selected="true"]');
selected =
CURRENT[
e.keyCode === 38 ? "previousElementSibling" : "nextElementSibling"
];
if (!selected)
selected =
CURRENT.parentNode[
e.keyCode === 38 ? "lastElementChild" : "firstElementChild"
];
CURRENT.setAttribute("aria-selected", false);
selected.setAttribute("aria-selected", true);
}
if (selected)
SEARCH.setAttribute(
"aria-activedescendant",
selected.getAttribute("data-option")
);
};
const renderOptions = (value) => {
length = value.length;
let options = [
...AVAILABLE_OPTIONS.filter((option) => {
return (
typeof option.title === "string" &&
option.title.toLowerCase().indexOf(value.toLowerCase()) !== -1
);
}),
value !== ""
? AVAILABLE_OPTIONS.filter((option) => option.id === "web-search")[0]
: null
];
// Regardless of what's happening, you're building the options...
buildOptions(options, value);
if (!OPTIONS.matches(":open") && options.length) {
OPTIONS.showPopover();
}
};
const handleActionClick = (e) => {
fireAction(
e.target.tagName === "I"
? e.target.parentNode.getAttribute("data-option")
: e.target.getAttribute("data-option")
);
};
const handleKeyboardInteraction = (e) => {
// Handle building of options
if (POPUP.matches(":open")) {
if (e.keyCode === 13 && e.type === "keydown") {
e.preventDefault();
fireAction(
document
.querySelector('[aria-selected="true"]')
.getAttribute("data-option")
);
} else if (SEARCH.value.length !== length) {
renderOptions(SEARCH.value);
} else if (
OPTIONS.matches(":open") &&
(e.keyCode === 38 || e.keyCode === 40) &&
e.type === "keydown"
) {
selectOption(e);
} else if (e.keyCode === 27) {
SEARCH.value = "";
}
}
};
POPUP.addEventListener("beforetoggle", (e) => {
if (e.newState === "open") onActivated(e);
});
window.addEventListener("keydown", handleKeyboardInteraction);
window.addEventListener("keypress", handleKeyboardInteraction);
window.addEventListener("keyup", handleKeyboardInteraction);
OPTIONS.addEventListener("beforetoggle", (e) => {
if (e.newState === "open") onOptionsOpen(e);
else onOptionsHide(e);
});
OPTIONS.addEventListener("pointermove", selectOption);
OPTIONS.addEventListener("click", handleActionClick);
// Catch case for clicking on the input
SEARCH.addEventListener("click", (e) => {
// Don't want the click on the input to close the popup
OPTIONS.showPopover();
});