Select

Native HTML <select> styled to match the system, plus a Combobox-backed custom select pattern for polished dropdown menus.

Overview

The Select component is a core building block of the Byteion design system, providing a robust, accessibility-compliant interface for its specific UI capabilities.

Why this exists

Native select elements are notoriously hard to style consistently across browsers. Byteion provides a unified appearance that matches the rest of the form system while preserving native behavior (keyboard navigation, mobile picker).

Mental model

A select is a single-choice input with a predefined list. Use it when options are known and finite. If options exceed 10, consider a combobox instead.

When to use

• Country, timezone, or category selection • Form fields with 3-10 known options • Filter dropdowns in data tables

When not to use

• Do not use for more than 10 options — use a combobox instead • Do not use for yes/no choices — use a switch or radio • Avoid for hierarchical selections — use a tree or cascading select

Basic example

The following example demonstrates standard usage, fully compliant with Byteion's token-first approach.

Variants

Each variant communicates a different meaning. Choose based on priority, not aesthetics.

  • outline — outline style variant.
  • filled — filled style variant.

Sizes

Size should match the importance and context of the component.

  • sm — sm size.
  • md — md size.
  • lg — lg size.

States

This component reacts seamlessly to the following interaction and data states:

  • default — Resting appearance before interaction.
  • hover — Pointer is over the element — visual feedback via color or shadow.
  • focus-visible — focus-visible state.
  • disabled — Non-interactive — grayed out, cursor not-allowed.
  • error — Validation failed — red border and error message.
  • success — Validation passed — green indicator.
  • invalid — Input value does not match expected format.

Anatomy

• Label — describes the selection • Select wrapper — provides border, radius, chevron icon • Native select — preserves browser picker on mobile • Options — list of predefined choices

Usage guidelines

  • Provide a default/placeholder option like"Select…".
  • Keep options under 10 for usability.
  • Use optgroup for categorized lists.

Do and Don't

Do

  • Use a clear placeholder option.
  • Order options logically (alphabetical or by frequency).
  • Use optgroup for large lists.

Don't

  • Do not exceed 10 options without providing search.
  • Do not use selects for boolean choices.
  • Do not disable the default browser picker on mobile.

Accessibility

Native selects inherit browser keyboard navigation. Label must be associated via for attribute.

Role:combobox

ARIA Attributes

  • aria-invalid
  • aria-required
  • aria-describedby
  • aria-label

Keyboard Navigation

  • Tab: Moves focus into and out of the field
  • Space / Enter / Down: Opens the option list
  • Arrow keys: Navigate options
  • Escape: Close without changing selection

Customization

Match input styling via the shared form token system.

Compose with React cva()

Use the exportedcva() +cn() utilities to compose your own variant on top of the Byteion tokens.

import { cva, cn, type VariantProps } from '@byteion/react';

const mySelect = cva('base classes…', {
 variants: {
  accent: { soft:'border-primary-200', loud:'border-primary-500' },
 },
 defaultVariants: { accent:'soft' },
});

type Props = React.HTMLAttributes<HTMLDivElement> & VariantProps<typeof mySelect>;

Compose with Vue cva()

Use the exportedcva() +cn() utilities inside standard Vue setup blocks.

<script setup lang="ts">
import { computed, useAttrs } from 'vue';
import { cva, type VariantProps } from '@byteion/vue';

interface Props { accent?:'soft' |'loud' }
const props = withDefaults(defineProps<Props>(), { accent:'soft' });
const attrs = useAttrs();

const mySelect = cva('base classes…', {
  variants: { accent: { soft:'border-primary-200', loud:'border-primary-500' } },
});

const cls = computed(() => mySelect({ accent: props.accent, class: attrs.class as string | undefined }));
</script>

Runtime events

Byteion keeps framework APIs idiomatic: HTML uses DOM custom events, React uses callback props, and Vue uses emitted events / v-model updates.

HTML / JS events

No HTML runtime events are declared by this adapter.

React callbacks

No React callback events are declared by this adapter.

Vue emits

  • update:modelValue[value: string]

Remote / AJAX

This component does not implement Byteion JS remote loading. Use normal HTML form submission, framework state, or your application data layer around it.

API Reference

Native HTML select element. No custom data attributes needed.

HTML Data Attributes

No custom data attributes required.

React Props · SelectProps

Extracted directly fromreact/src/components/Select.tsx.

export interface SelectProps
  extends Omit<SelectHTMLAttributes<HTMLSelectElement>, 'size' | 'children'>,
    VariantProps<typeof selectStyles> {
  label?: ReactNode;
  helperText?: ReactNode;
  error?: ReactNode;
  success?: ReactNode;
  /** Either pass `options` or use `children` for raw `<option>`/`<optgroup>` elements. */
  options?: SelectOption[];
  /** Placeholder shown when no value is selected. Renders as a disabled first option. */
  placeholder?: string;
  children?: ReactNode;
}

React Variant Schema

Internal variant composition defined viacva().

cva(
  'block w-full appearance-none rounded-lg border bg-card pl-3.5 pr-9 text-sm font-medium text-foreground outline-none transition-colors duration-200 ease-out-soft shadow-field hover:shadow-field-hover focus-visible:shadow-field-focus dark:border-border dark:bg-input dark:text-foreground dark:shadow-field-dark dark:hover:shadow-field-dark-hover dark:focus-visible:shadow-field-focus-dark',
  {
    variants: {
      variant: {
        outline: 'border-border hover:border-border focus-visible:border-primary-400 dark:focus-visible:border-primary-500 dark:hover:border-border',
        filled:  'border-border bg-muted/60 hover:border-border hover:bg-muted focus-visible:border-primary-400 dark:border-border dark:bg-input dark:hover:border-border dark:hover:bg-input dark:focus-visible:border-primary-500',
      },
      size: {
        sm: 'h-9 rounded-md pl-3 pr-8 text-sm',
        md: 'h-10 pl-3.5 pr-9 text-sm',
        lg: 'h-11 pl-4 pr-10 text-base',
        xl: 'h-12 rounded-xl pl-4 pr-10 text-base',
      },
      state: {
        default: '',
        error:   '!border-danger-400 focus-visible:!shadow-field-error',
        success: '!border-success-400 focus-visible:!shadow-field-success',
      },
    },
    defaultVariants: { variant: 'outline', size: 'md', state: 'default' },
  },
)

Vue Props · SelectProps

Extracted directly fromvue/src/components/Select.vue.

export interface SelectProps {
  modelValue?: string;
  defaultValue?: string;
  options?: SelectOption[];
  variant?: 'outline' | 'filled';
  size?: 'sm' | 'md' | 'lg';
  state?: 'default' | 'error' | 'success';
  label?: string;
  helperText?: string;
  error?: string;
  success?: string;
  placeholder?: string;
  required?: boolean;
  id?: string;
}

Developer tips

• Edge case: on iOS, native select opens a bottom sheet — this is the best UX for mobile. • Best practice: sort options by frequency of use, not alphabetically, when one option is much more common.

Common mistakes

Avoid hardcoded neutral colors, placeholder-only labels, duplicated primary actions, and missing keyboard states. Keep this component on Byteion tokens so dashboard, admin, POS, ERP, ecommerce back-office, Laravel, React, and Vue starter-kit screens can be rebranded without rewriting markup.

Production usage tips

Use the canonical HTML as the source of truth, keep React and Vue wrappers aligned with the documented variants, test dark mode and branded themes before release, and wire interactive behavior through data-bi-* attributes instead of styling hooks.

Premium examples

Advanced patterns available in Byteion Pro:

• Multi-column select with icons and descriptions • Virtualized select for 1000+ options

Unlock Premium Patterns

Get access to copy-paste ready Pro blocks, SaaS templates, and advanced data-tables built with this component.

Explore Pro Components