Playground de customizable select en CSS

Explora los nuevos select personalizables con contenido enriquecido en las opciones, estilos para el picker, el icono y el checkmark, y un fallback opcional para navegadores sin soporte.

Preguntar a la IA

Playground de customizable select en CSS

Respuesta generada con openai/gpt-5.4-nano. La IA puede cometer errores. Revisa siempre el resultado.
HTML
<form class="custom-select-demo">
  <label for="language-select">Lenguaje principal</label>
  <select class="language-select" id="language-select" name="language">
    <button>
      <selectedcontent></selectedcontent>
    </button>
      <option value="javascript" data-language="javascript">
        <span class="language-option" data-language="javascript">
          <span class="logo" data-logo="JS" aria-hidden="true"></span>
          <span class="language-copy">
            <span class="name">JavaScript</span>
            <span class="meta" data-meta="Runtime de frontend"></span>
          </span>
        </span>
      </option>
      <option value="typescript" data-language="typescript" selected>
        <span class="language-option" data-language="typescript">
          <span class="logo" data-logo="TS" aria-hidden="true"></span>
          <span class="language-copy">
            <span class="name">TypeScript</span>
            <span class="meta" data-meta="Tipado estático"></span>
          </span>
        </span>
      </option>
      <option value="python" data-language="python">
        <span class="language-option" data-language="python">
          <span class="logo" data-logo="Py" aria-hidden="true"></span>
          <span class="language-copy">
            <span class="name">Python</span>
            <span class="meta" data-meta="Scripting y datos"></span>
          </span>
        </span>
      </option>
      <option value="rust" data-language="rust">
        <span class="language-option" data-language="rust">
          <span class="logo" data-logo="Rs" aria-hidden="true"></span>
          <span class="language-copy">
            <span class="name">Rust</span>
            <span class="meta" data-meta="Sistemas seguros"></span>
          </span>
        </span>
      </option>
      <option value="go" data-language="go">
        <span class="language-option" data-language="go">
          <span class="logo" data-logo="Go" aria-hidden="true"></span>
          <span class="language-copy">
            <span class="name">Go</span>
            <span class="meta" data-meta="Servicios concurrentes"></span>
          </span>
        </span>
      </option>
  </select>
</form>
CSS
.custom-select-demo {
  display: grid;
  gap: 0.55rem;
  inline-size: min(100%, 28rem);
}

.custom-select-demo > label {
  color: color-mix(in oklch, currentColor 72%, transparent);
  font-weight: 750;
}

.language-select {
  inline-size: fit-content;
  max-inline-size: 100%;
  font: inherit;
  color: #f8fafc;
}

@supports (appearance: base-select) {
  .language-select,
  .language-select::picker(select) {
    appearance: base-select;
  }

  .language-select {
    display: flex;
    align-items: center;
    gap: 10px;
    padding-block: 12px;
    padding-inline: 16px;
    border: 1px solid color-mix(in oklch, #8b5cf6 52%, transparent);
    border-radius: 18px;
    background: #111827;
    color: #f8fafc;
    box-shadow: 0 1rem 2.5rem color-mix(in oklch, #8b5cf6 28%, transparent);
  }

  .language-select selectedcontent {
    display: contents;
  }

  .language-select .language-option {
    display: flex;
    align-items: center;
    gap: 10px;
    min-inline-size: max-content;
  }

  .language-select::picker-icon {
    content: '';
    width: 1.5rem;
    aspect-ratio: 1;
    background-image: url('/caret-down.svg');
    background-size: cover;
    filter: brightness(0) invert(1);
    translate: 0.3125rem;
    transition: transform 0.2s ease-out;
  }

  .language-select:open::picker-icon {
    transform: rotateX(180deg);
  }

  .language-select::picker(select) {
    margin-block-start: 0.45rem;
    padding: 0.38rem;
    border: 1px solid color-mix(in oklch, #8b5cf6 46%, transparent);
    border-radius: 18px;
    background: #111827;
    color: #f8fafc;
    box-shadow: 0 1.25rem 3rem color-mix(in oklch, #8b5cf6 32%, black 18%);
    opacity: 0;
    translate: 0 -0.35rem;
    transition: opacity 0.2s ease, translate 0.2s ease, display 0.2s allow-discrete, overlay 0.2s allow-discrete;
  }

  .language-select::picker(select) {
    inline-size: fit-content;
    min-inline-size: anchor-size(width);
    max-inline-size: min(28rem, calc(100vi - 2rem));
  }

  .language-select:open::picker(select) {
    opacity: 1;
    translate: 0 0;
  }

  @starting-style {
    .language-select:open::picker(select) {
      opacity: 0;
      translate: 0 -0.35rem;
    }
  }

  .language-select option {
    display: flex;
    align-items: center;
    gap: 10px;
    padding-block: 10px;
    padding-inline: 14px;
    border-radius: 12px;
    background: transparent;
    color: #f8fafc;
    transition: background-color 0.2s ease, color 0.2s ease;
  }

  .language-select option:is(:hover, :focus-visible) {
    background: color-mix(in oklch, #8b5cf6 22%, transparent);
  }

  .language-select option:checked {
    background: color-mix(in oklch, #8b5cf6 34%, transparent);
  }

  .language-select option::checkmark {
    content: '✓';
    display: grid;
    place-items: center;
    inline-size: 1.45rem;
    block-size: 1.45rem;
    flex: none;
    order: 2;
    margin-inline-start: 0.35rem;
    border-radius: 999px;
    background: #8b5cf6;
    color: #111827;
    font-weight: 800;
  }

  .language-select .logo {
    display: grid;
    place-items: center;
    inline-size: 34px;
    block-size: 34px;
    border-radius: 0.75rem;
    background: var(--logo-bg);
    color: var(--logo-color);
    font-weight: 900;
    letter-spacing: 0;
  }

  .language-select .logo::before {
    content: attr(data-logo);
    font-size: 0.72em;
  }

  .language-select .language-copy {
    display: grid;
    gap: 0.08rem;
    min-inline-size: max-content;
  }

  .language-select .name {
    font-weight: 800;
    line-height: 1.1;
    white-space: nowrap;
  }

  .language-select .meta {
    font-size: 0.78rem;
    color: color-mix(in oklch, currentColor 70%, transparent);
    line-height: 1.1;
    white-space: nowrap;
  }

  .language-select .meta::before {
    content: attr(data-meta);
  }

  .language-select [data-language="javascript"] .logo {
    --logo-bg: #f7df1e;
    --logo-color: #1f2937;
  }

  .language-select [data-language="typescript"] .logo {
    --logo-bg: #3178c6;
    --logo-color: #f8fafc;
  }

  .language-select [data-language="python"] .logo {
    --logo-bg: #3776ab;
    --logo-color: #fff7d6;
  }

  .language-select [data-language="rust"] .logo {
    --logo-bg: #f97316;
    --logo-color: #111827;
  }

  .language-select [data-language="go"] .logo {
    --logo-bg: #00add8;
    --logo-color: #06202a;
  }
}

@supports not (appearance: base-select) {
  .language-select {
    appearance: none;
    padding-block: 12px;
    padding-inline: 16px calc(16px + 1.75rem);
    border: 1px solid color-mix(in oklch, #8b5cf6 52%, transparent);
    border-radius: 18px;
    background: #111827 url("data:image/svg+xml,%3Csvg%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20width%3D%2224%22%20height%3D%2224%22%20viewBox%3D%220%200%2024%2024%22%20fill%3D%22%23f8fafc%22%3E%3Cpath%20stroke%3D%22none%22%20d%3D%22M0%200h24v24H0z%22%20fill%3D%22none%22%2F%3E%3Cpath%20d%3D%22M18.707%208.293a1%201%200%200%201%200%201.414l-6%206a1%201%200%200%201-1.414%200l-6-6a1%201%200%200%201%201.414-1.414L12%2013.586l5.293-5.293a1%201%200%200%201%201.414%200%22%2F%3E%3C%2Fsvg%3E") right 0.625rem center / 1.5rem no-repeat;
    color: #f8fafc;
  }
}