Contextual Action

Action relevant to current selection/context.

Q3-report.pdf

html

<div class="ctx-row">
  <span class="ctx-row__name">Q3-report.pdf</span>
  <button type="button" class="ctx-action ctx-action--onHover" style="--bg:#241430;border-radius:8px">
    <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 20l1-4L15 6l3 3L8 19l-4 1z"/><path d="M14 6l3 3"/></svg> Rename
  </button>
</div>
<style>
.ctx-row {
  display: flex; align-items: center; justify-content: space-between;
  padding: 12px 14px; border: 1px solid rgba(36,20,48,.15);
  font: 500 14px/1 system-ui, sans-serif; color: #241430;
}
.ctx-action {
  display: inline-flex; align-items: center; gap: 6px;
  font: 600 13px/1 system-ui, sans-serif;
  padding: 7px 12px;
  border: 1px solid var(--bg);
  background: transparent;
  color: var(--bg);
  cursor: pointer;
}
.ctx-action--onHover { opacity: 0; }
@media (prefers-reduced-motion: no-preference) {
.ctx-action--onHover { transition: opacity .12s ease; }
}
.ctx-row:hover .ctx-action--onHover,
.ctx-action--onHover:focus-visible { opacity: 1; }
.ctx-action:hover:not(:disabled) { background: rgba(36,20,48,.08); }
.ctx-action:focus-visible { outline:2px solid #241430;outline-offset:2px; }
.ctx-action:disabled { opacity: .35; cursor: not-allowed; }
</style>

tailwind

<div class="group flex items-center justify-between px-3.5 py-3 text-sm font-medium text-[#241430]" style="border:1px solid rgba(36,20,48,.15)">
  <span>Q3-report.pdf</span>
  <button type="button" class="inline-flex items-center gap-1.5 px-3 py-1.75 text-xs font-semibold bg-transparent cursor-pointer disabled:opacity-35 disabled:cursor-not-allowed group-hover:opacity-100 ctx-action-tw ctx-action-tw--onHover" style="border:1px solid #241430;color:#241430;border-radius:8px">
    <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M4 20l1-4L15 6l3 3L8 19l-4 1z"/><path d="M14 6l3 3"/></svg> Rename
  </button>
</div>
<style>
.ctx-action-tw--onHover { opacity: 0; }
@media (prefers-reduced-motion: no-preference) {
.ctx-action-tw--onHover { transition: opacity .12s ease; }
}
.group:hover .ctx-action-tw--onHover, .ctx-action-tw--onHover:focus-visible { opacity: 1; }
.ctx-action-tw--onHover:focus-visible { outline:2px solid #241430;outline-offset:2px; }
</style>

react

export function ContextualActionButton({
  label = "Rename",
  visibility = "onHover",
  bg = "#241430",
  radius = 8,
  disabled = false,
}) {
  const alwaysVisible = visibility === "alwaysVisible";
  return (
    <div style={{ display: "flex", alignItems: "center", justifyContent: "space-between", padding: "12px 14px", border: "1px solid rgba(36,20,48,.15)", font: "500 14px/1 system-ui, sans-serif", color: "#241430" }}>
      <span>Q3-report.pdf</span>
      <button
        type="button"
        disabled={disabled}
        className={alwaysVisible ? "" : "reveal-on-hover"}
        style={{
          display: "inline-flex",
          alignItems: "center",
          gap: 6,
          padding: "7px 12px",
          borderRadius: radius,
          border: `1px solid ${bg}`,
          background: "transparent",
          color: bg,
          font: "600 13px/1 system-ui, sans-serif",
          cursor: disabled ? "not-allowed" : "pointer",
          opacity: alwaysVisible ? 1 : undefined,
        }}
      >
        <svg width="15" height="15" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2" strokeLinecap="round" strokeLinejoin="round" aria-hidden="true">
          <path d="M4 20l1-4L15 6l3 3L8 19l-4 1z" /><path d="M14 6l3 3" />
        </svg>
        {label}
      </button>
    </div>
  );
}