Popover

Contextual floating panel.

html

<div class="popover-demo">
  <button type="button" class="popover-trigger" aria-haspopup="dialog" aria-controls="popover-panel" aria-expanded="true">Filters</button>
  
  <div class="popover-panel popover-panel--light" id="popover-panel" role="dialog" aria-labelledby="popover-panel-title" style="width:240px;border-color:#BFD9F2;">
    <div class="popover-panel__head">
      <span class="popover-panel__title" id="popover-panel-title">Filters</span>
      <button type="button" class="popover-panel__close" aria-label="Close">
        <svg width="12" height="12" viewBox="0 0 12 12" aria-hidden="true"><path d="M1 1l10 10M11 1L1 11" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>
      </button>
    </div>
    <p class="popover-panel__body">Choose how results are sorted before exporting.</p>
  </div>
  
</div>
<style>
.popover-demo { position: relative; min-height: 220px; padding: 22px; box-sizing: border-box; }
.popover-trigger { font: 500 14px system-ui, sans-serif; padding: 9px 16px; border-radius: 8px; border: 1.5px solid #24143033; background: #FAF7F2; color: #241430; cursor: pointer; }
.popover-trigger:focus-visible { outline: 2px solid #241430; outline-offset: 2px; }
.popover-panel { position: absolute; top: 78px; left: 22px; border-radius: 12px; border: 1.5px solid; box-sizing: border-box; box-shadow: 0 16px 32px rgba(36,20,48,.22); padding: 14px 16px; }
.popover-panel--light { background: #FAF7F2; color: #241430; }
.popover-panel--dark { background: #241430; color: #FAF7F2; }
.popover-panel--accent { background: #FAF7F2; color: #241430; border-width: 3px; }
.popover-panel__head { display: flex; align-items: center; justify-content: space-between; gap: 12px; margin-bottom: 8px; }
.popover-panel__title { font-weight: 600; font-size: 14px; }
.popover-panel__close { border: none; background: transparent; color: inherit; cursor: pointer; padding: 4px; border-radius: 6px; display: grid; place-items: center; }
.popover-panel__close:focus-visible { outline: 2px solid currentColor; outline-offset: 1px; }
.popover-panel__body { margin: 0; font-size: 13px; line-height: 1.5; opacity: .85; }
</style>

tailwind

<div class="relative min-h-[220px] p-[22px] box-border">
  <button type="button" class="px-4 py-2.5 rounded-lg text-sm font-medium border-[1.5px] border-[#24143033] bg-[#FAF7F2] text-[#241430]" aria-haspopup="dialog" aria-controls="popover-tw-panel" aria-expanded="true">Filters</button>
  
  <div id="popover-tw-panel" role="dialog" aria-labelledby="popover-tw-title" class="popover-tw-panel popover-tw-panel--light absolute top-[78px] left-[22px] rounded-xl border-[1.5px] box-border shadow-2xl p-4" style="width:240px;border-color:#BFD9F2;">
    <div class="flex items-center justify-between gap-3 mb-2">
      <span id="popover-tw-title" class="font-semibold text-sm">Filters</span>
      <button type="button" aria-label="Close" class="border-0 bg-transparent cursor-pointer p-1 rounded-md grid place-items-center text-current">
        <svg width="12" height="12" viewBox="0 0 12 12" aria-hidden="true"><path d="M1 1l10 10M11 1L1 11" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/></svg>
      </button>
    </div>
    <p class="m-0 text-[13px] leading-relaxed opacity-85">Choose how results are sorted before exporting.</p>
  </div>
  
</div>
<style>
.popover-tw-panel--light { background: #FAF7F2; color: #241430; }
.popover-tw-panel--dark { background: #241430; color: #FAF7F2; }
.popover-tw-panel--accent { background: #FAF7F2; color: #241430; border-width: 3px; }
</style>

react

import { useState } from "react";

export function Popover({
  label = "Filters",
  body = "Choose how results are sorted before exporting.",
  variant = "light",
  width = 240,
  accent = "#BFD9F2",
  open: initialOpen = true,
}) {
  const [open, setOpen] = useState(initialOpen);
  const panelStyle = {
    light: { background: "#FAF7F2", color: "#241430" },
    dark: { background: "#241430", color: "#FAF7F2" },
    accent: { background: "#FAF7F2", color: "#241430", borderWidth: 3 },
  }[variant] ?? {};
  return (
    <div style={{ position: "relative", minHeight: 220, padding: 22, boxSizing: "border-box" }}>
      <button type="button" onClick={() => setOpen((o) => !o)} aria-haspopup="dialog" aria-controls="popover-panel"
        aria-expanded={open ? "true" : "false"} style={{ font: "500 14px system-ui, sans-serif", padding: "9px 16px",
        borderRadius: 8, border: "1.5px solid #24143033", background: "#FAF7F2", color: "#241430", cursor: "pointer" }}>
        {label}
      </button>
      {open && (
        <div id="popover-panel" role="dialog" aria-labelledby="popover-panel-title" style={{ position: "absolute",
          top: 78, left: 22, borderRadius: 12, border: "1.5px solid", borderColor: accent, boxSizing: "border-box",
          boxShadow: "0 16px 32px rgba(36,20,48,.22)", padding: "14px 16px", width, ...panelStyle }}>
          <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", gap: 12, marginBottom: 8 }}>
            <span id="popover-panel-title" style={{ fontWeight: 600, fontSize: 14 }}>{label}</span>
            <button type="button" aria-label="Close" onClick={() => setOpen(false)} style={{ border: "none",
              background: "transparent", color: "inherit", cursor: "pointer", padding: 4, borderRadius: 6 }}>
              <svg width="12" height="12" viewBox="0 0 12 12" aria-hidden="true"><path d="M1 1l10 10M11 1L1 11" stroke="currentColor" strokeWidth="1.6" strokeLinecap="round" /></svg>
            </button>
          </div>
          <p style={{ margin: 0, fontSize: 13, lineHeight: 1.5, opacity: .85 }}>{body}</p>
        </div>
      )}
    </div>
  );
}