№ 013

Indexed Drawer

A pixel search field with a drawer of index cards hidden inside it. ⌘K to open.

Version
1.0.0
Download
12 KB · 3 files
Added
Oct 26, 2026
Built with
HTML, CSS, JS
Needs
Google Fonts
Tags
search, command palette, pixel

Usage

Copy indexed-drawer.css and indexed-drawer.js (and assets/, if there is one) into your project, then:

  new IndexedDrawer(element, {
    items: [{ title: 'Music Library', type: 'FOLDER' }, ...],
    placeholder: 'Search files...',
    rows: 3,                 // cards visible at first; pull the drawer's front panel to show more
    onSelect(item) {},
  })

A pixel search field with a drawer hidden inside it. Focus it (or press ⌘/Ctrl + K) and the drawer
slides out beneath the field, its index cards rising into their slots. Typing filters immediately,
arrow keys move the highlight, Enter opens the highlighted card, Escape or a click outside closes.
Drag the front panel down (or Alt + ↓/↑) to pull the drawer further out and see more cards at once;
it snaps to whole rows. Longer lists scroll with the arrow keys or the wheel.
The animation is only feedback: results can be read and chosen from the first keystroke.

Source

/* Indexed Drawer — a pixel search field with a drawer inside. Laid out on a 360-unit grid (--u),
   the field's design width, so it scales as one piece with its container. The drawer hangs below the
   field without taking up layout space, like any dropdown. */

.id {
  --id-paper: #f1ede3;
  --id-paper-lt: #f6f3eb;
  --id-paper-dk: #ddd6c8;
  --id-drawer: #ece7dc;
  --id-lip: #c9c2b5;
  --id-ink: #222a28;
  --id-line: #a59f97;
  --id-muted: #6f6a62;
  --id-sage: #a5b7a5;
  --id-sage-dk: #839684;
  --id-coral: #e27c62;
  --id-shadow: rgba(60, 54, 44, 0.24);
  --id-bitmap: "DotGothic16", "Silkscreen", ui-monospace, monospace;
  --id-mono: "IBM Plex Mono", ui-monospace, monospace;

  container-type: inline-size;
  position: relative;
  width: 100%;
  color: var(--id-ink);
}

.id *,
.id *::before,
.id *::after {
  box-sizing: border-box;
}

.id-stage {
  --u: calc(100cqw / 360);
  position: relative;
  width: 100%;
  height: calc(var(--u) * 56);
}

/* one-pixel stepped corners: the corner square the size of the border is cut away */
.id-field,
.id-body,
.id-lip,
.id-card {
  --st: calc(var(--u) * 2);
  clip-path: polygon(
    var(--st) 0, calc(100% - var(--st)) 0, calc(100% - var(--st)) var(--st), 100% var(--st),
    100% calc(100% - var(--st)), calc(100% - var(--st)) calc(100% - var(--st)), calc(100% - var(--st)) 100%,
    var(--st) 100%, var(--st) calc(100% - var(--st)), 0 calc(100% - var(--st)), 0 var(--st), var(--st) var(--st)
  );
}

/* ── field ── */

.id-fieldwrap {
  position: absolute;
  inset: 0 0 auto;
  height: calc(var(--u) * 52);
  z-index: 2;
  filter: drop-shadow(calc(var(--u) * 4) calc(var(--u) * 4) 0 var(--id-shadow));
}

.id-field {
  position: absolute;
  inset: 0;
  border: calc(var(--u) * 2) solid var(--id-ink);
  background: var(--id-paper);
  box-shadow: inset 0 calc(var(--u) * -4) 0 var(--id-paper-dk), inset 0 calc(var(--u) * 2) 0 var(--id-paper-lt);
  cursor: text;
}

.is-active .id-field {
  box-shadow: inset 0 calc(var(--u) * -4) 0 var(--id-paper-dk), inset 0 0 0 calc(var(--u) * 2) var(--id-sage);
}

.id-icon {
  position: absolute;
  left: calc(var(--u) * 14);
  top: calc(var(--u) * 14);
  width: calc(var(--u) * 18);
  fill: var(--id-ink);
}

.id-icon svg { display: block; width: 100%; }

.id-rule {
  position: absolute;
  left: calc(var(--u) * 42);
  top: calc(var(--u) * 11);
  width: calc(var(--u) * 1.5);
  height: calc(var(--u) * 26);
  background: #8a827a;
}

.id-rule-r {
  left: auto;
  right: calc(var(--u) * 64);
}

.id-entry {
  position: absolute;
  left: calc(var(--u) * 52);
  right: calc(var(--u) * 72);
  top: 0;
  bottom: calc(var(--u) * 2);
  display: flex;
  align-items: center;
}

.id-input,
.id-mirror {
  font: 400 calc(var(--u) * 15) / 1 var(--id-bitmap);
  letter-spacing: 0.04em;
}

.id-input {
  position: relative;
  z-index: 1;
  width: 100%;
  padding: 0;
  border: 0;
  outline: none;
  background: transparent;
  color: var(--id-ink);
  caret-color: transparent;   /* the caret is drawn as a pixel block */
}

.id-input::placeholder {
  color: #8d877e;
  opacity: 1;
}

/* invisible copy of the text: measures where the caret goes, and shows select-all */
.id-mirror {
  position: absolute;
  left: 0;
  white-space: pre;
  color: transparent;
  visibility: hidden;
}

.is-all .id-mirror {
  visibility: visible;
  background: var(--id-sage);
  box-shadow: 0 0 0 calc(var(--u) * 2) var(--id-sage);
}

.id-caret {
  position: absolute;
  top: 50%;
  width: calc(var(--u) * 2.5);
  height: calc(var(--u) * 18);
  margin-top: calc(var(--u) * -9);
  margin-left: calc(var(--u) * 1);
  background: var(--id-coral);
}

.id-kbd {
  position: absolute;
  right: calc(var(--u) * 12);
  top: calc(var(--u) * 11);
  width: calc(var(--u) * 42);
  height: calc(var(--u) * 26);
  display: grid;
  place-items: center;
  border: calc(var(--u) * 1.5) solid #c4beb3;
  font: 500 calc(var(--u) * 11) / 1 var(--id-mono);
  letter-spacing: 0.12em;
  color: var(--id-ink);
}

.is-active .id-kbd { visibility: hidden; }

@media (hover: none) {
  .id-kbd,
  .id-rule-r { display: none; }
  .id-entry { right: calc(var(--u) * 16); }
}

/* ── drawer ── */

.id-well {
  --recess: 0;
  position: absolute;
  left: 0;
  right: 0;
  top: calc(var(--u) * 50);   /* tucked under the field's bottom border */
  height: calc(var(--u) * 470);   /* room for the drawer pulled out to eight rows */
  clip-path: inset(0 -10% -10% -10%);
  pointer-events: none;
  z-index: 1;
}

/* the dark recess the drawer slides out of */
.id-well::before {
  content: "";
  position: absolute;
  left: calc(var(--u) * 22);
  right: calc(var(--u) * 22);
  top: 0;
  height: calc(var(--u) * 4);
  background: var(--id-ink);
  opacity: var(--recess);
}

.id-drawer {
  --body: 180;
  position: absolute;
  inset: 0 0 auto;
}

/* hard shadow behind the body and the front panel, as plain layers: a drop-shadow filter over these
   clipped shapes sometimes failed to paint the body at all (the drawer blinked in recordings) */
.id-drawer::before,
.id-drawer::after {
  content: "";
  position: absolute;
  background: var(--id-shadow);
}

.id-drawer::before {
  left: calc(var(--u) * 28);
  right: calc(var(--u) * 20);
  top: calc(var(--u) * 4);
  height: calc(var(--u) * var(--body));
}

.id-drawer::after {
  left: calc(var(--u) * 24);
  right: calc(var(--u) * 16);
  top: calc(var(--u) * (var(--body) + 4));
  height: calc(var(--u) * 18);
}

.is-open .id-drawer { pointer-events: auto; }

/* the drawer is a little narrower than the field, as if sliding inside a housing */
.id-body {
  position: absolute;
  left: calc(var(--u) * 24);
  right: calc(var(--u) * 24);
  top: 0;
  height: calc(var(--u) * var(--body));
  border: calc(var(--u) * 2) solid var(--id-ink);
  border-bottom: 0;
  background: var(--id-drawer);
  box-shadow: inset 0 calc(var(--u) * 3) 0 var(--id-paper-dk);
}

.id-head {
  position: absolute;
  left: calc(var(--u) * 12);
  right: calc(var(--u) * 12);
  top: calc(var(--u) * 12);
  display: flex;
  align-items: center;
  justify-content: space-between;
}

.id-range {
  margin-left: auto;
  margin-right: calc(var(--u) * 10);
  font: 500 calc(var(--u) * 9) / 1 var(--id-mono);
  letter-spacing: 0.12em;
  color: var(--id-muted);
}

.id-count {
  font: 500 calc(var(--u) * 9) / 1 var(--id-mono);
  letter-spacing: 0.16em;
  color: var(--id-ink);
  white-space: nowrap;
}

.id-dots {
  display: flex;
  gap: calc(var(--u) * 3);
}

.id-dots i {
  width: calc(var(--u) * 5);
  height: calc(var(--u) * 8);
  background: #b9b2a6;
}

.id-dots i.is-on { background: var(--id-coral); }

.id-list {
  position: absolute;
  left: calc(var(--u) * 10);
  right: calc(var(--u) * 10);
  top: calc(var(--u) * 32);
  margin: 0;
  padding: 0;
  list-style: none;
  display: flex;
  flex-direction: column;
  gap: calc(var(--u) * 8);
}

.id-card {
  position: relative;
  height: calc(var(--u) * 40);
  display: flex;
  align-items: center;
  border: calc(var(--u) * 1.5) solid var(--id-line);
  background: var(--id-paper);
  cursor: pointer;
}

.id-card[hidden] { display: none; }

.id-card.is-selected {
  background: var(--id-sage);
  border-color: var(--id-sage-dk);
}

.id-card.is-pressed {
  border-color: var(--id-ink);
  background: var(--id-sage-dk);
}

.id-num {
  width: calc(var(--u) * 33);
  padding-left: calc(var(--u) * 10);
  font: 400 calc(var(--u) * 11.5) / 1 var(--id-bitmap);
  letter-spacing: 0.06em;
}

.id-num::after {
  content: "";
  position: absolute;
  left: calc(var(--u) * 34);
  top: calc(var(--u) * 10);
  width: calc(var(--u) * 1.5);
  height: calc(var(--u) * 17);
  background: currentColor;
  opacity: 0.6;
}

.id-title {
  flex: 1;
  min-width: 0;
  padding-left: calc(var(--u) * 12);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
  font: 400 calc(var(--u) * 13) / 1 var(--id-bitmap);
  letter-spacing: 0.08em;
  text-transform: uppercase;
}

.id-type {
  width: calc(var(--u) * 54);
  font: 500 calc(var(--u) * 8.5) / 1 var(--id-mono);
  letter-spacing: 0.18em;
}

.id-card svg {
  width: calc(var(--u) * 5);
  margin-right: calc(var(--u) * 10);
  fill: var(--id-coral);
  visibility: hidden;
}

.id-card.is-selected svg { visibility: inherit; }   /* hidden with its card while it rises */

.id-empty {
  position: absolute;
  left: 0;
  right: 0;
  top: calc(var(--u) * 32);
  height: calc(var(--u) * 40);
  margin: 0;
  display: grid;
  place-items: center;
  font: 500 calc(var(--u) * 9.5) / 1 var(--id-mono);
  letter-spacing: 0.14em;
  color: var(--id-muted);
}

.id-empty[hidden] { display: none; }

/* front panel, a touch wider than the body */
.id-lip {
  position: absolute;
  left: calc(var(--u) * 20);
  right: calc(var(--u) * 20);
  top: calc(var(--u) * var(--body));
  height: calc(var(--u) * 18);
  border: calc(var(--u) * 2) solid var(--id-ink);
  background: var(--id-lip);
  box-shadow: inset 0 calc(var(--u) * 2) 0 #dbd4c7;
}

/* three short ridges in the middle of the panel: something to pull */
.id-grip {
  position: absolute;
  left: 50%;
  top: calc(var(--u) * 3.5);
  width: calc(var(--u) * 28);
  height: calc(var(--u) * 7);
  margin-left: calc(var(--u) * -14);
  background: repeating-linear-gradient(to bottom, #8f887c 0 calc(var(--u) * 1.5), transparent 0 calc(var(--u) * 3));
}

.id-lip:has(~ .id-pull:hover) .id-grip,
.is-pulling .id-grip {
  background: repeating-linear-gradient(to bottom, var(--id-ink) 0 calc(var(--u) * 1.5), transparent 0 calc(var(--u) * 3));
}

/* the grab area: the panel plus a margin above and below, so the thin panel is easy to catch */
.id-pull {
  position: absolute;
  left: calc(var(--u) * 20);
  right: calc(var(--u) * 20);
  top: calc(var(--u) * (var(--body) - 8));
  height: calc(var(--u) * 34);
  cursor: ns-resize;
  touch-action: none;
}

.is-pulling,
.is-pulling * { cursor: ns-resize !important; }

.id-live {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
}