Examples

Select Label Composition

Select trigger composition with static field label plus selected value.

Last reviewed: 2026-04-17 · Owner: docs@nake-ui

Adapters: react, vanilla

Test command: pnpm --filter @nake-ui/site test:unit

Example Files

Each file is copyable and downloadable.

vanilla/main.ts
import "@nake-ui/theme-vanilla/styles.css";
import { bindSelect } from "@nake-ui/adapter-vanilla";

interface AspectOption {
  id: string;
  label: string;
}

const ASPECT_OPTIONS: readonly AspectOption[] = [
  { id: "16:9", label: "16:9" },
  { id: "9:16", label: "9:16" },
  { id: "1:1", label: "1:1" }
];

const REGISTRATIONS = ASPECT_OPTIONS.map((option) => ({
  id: option.id,
  value: option.id,
  label: option.label
}));

export function mountSelectLabelComposition(root: HTMLElement): () => void {
  root.innerHTML = `
    <div data-ui="select">
      <button type="button" data-slot="trigger" aria-label="Select canvas aspect ratio">
        <span>Aspect</span>
        <span aria-hidden="true">: </span>
        <span data-slot="value">16:9</span>
        <span aria-hidden="true"> v</span>
      </button>
      <input type="hidden" data-slot="hidden-input" name="editor-aspect" />
      <div data-slot="content" hidden style="display: none; pointer-events: none;">
        ${ASPECT_OPTIONS.map(
          (option) =>
            `<button type="button" data-slot="option" data-demo-id="${option.id}" data-demo-label="${option.label}">${option.label}</button>`
        ).join("\n")}
      </div>
    </div>
  `;

  const trigger = root.querySelector<HTMLElement>("[data-slot='trigger']");
  const valueNode = root.querySelector<HTMLElement>("[data-slot='value']");
  const content = root.querySelector<HTMLElement>("[data-slot='content']");
  const hiddenInput = root.querySelector<HTMLInputElement>("[data-slot='hidden-input']");
  const optionElements = Array.from(
    root.querySelectorAll<HTMLElement>("[data-slot='option'][data-demo-id]")
  );

  if (!trigger || !valueNode || !content || !hiddenInput) {
    return () => {};
  }

  const optionNodes: Record<string, HTMLElement> = {};
  for (const optionElement of optionElements) {
    const id = optionElement.dataset.demoId;
    if (!id) {
      continue;
    }

    optionNodes[id] = optionElement;
  }

  const binding = bindSelect(
    {
      trigger,
      content,
      options: optionNodes,
      hiddenInput
    },
    REGISTRATIONS,
    {
      id: "docs-select-label-composition-controller",
      name: "editor-aspect",
      defaultValue: "16:9",
      onValueChange(nextValue) {
        if (nextValue === null) {
          valueNode.textContent = "16:9";
          return;
        }

        valueNode.textContent = optionNodes[nextValue]?.dataset.demoLabel ?? nextValue;
      }
    }
  );

  return () => {
    binding.destroy();
  };
}