Logo

Select

Button-triggered listbox selection built with composable primitives.

Preview

Usage

TS

import { FormControl, ReactiveFormsModule } from '@angular/forms';
import { FrSelectModule } from '@frame-ui-ng/components/select';

frameworkControl = new FormControl<string | null>('angular');

HTML

<button [frSelect]="frameworkMenu" [formControl]="frameworkControl" indicatorPosition="end" type="button">
  <frame-select-value placeholder="Choose a framework"></frame-select-value>
  <span frSelectIcon>
    <ng-icon name="tablerChevronDown" size="16" />
  </span>
</button>

<ng-template #frameworkMenu="frSelectContent" frSelectContent>
  <frame-select-panel>
    <frame-select-group>
      <button frSelectItem value="angular" label="Angular">
        <span>Angular</span>
      </button>
      <button frSelectItem value="react" label="React">
        <span>React</span>
      </button>
    </frame-select-group>
  </frame-select-panel>
</ng-template>

Examples

Basic select

Use a trigger, placeholder, and a small item list when the selected value should always be one choice from a fixed set.

Grouped items

Use labels and separators when the option list has meaningful categories that help people scan faster.

Invalid with reactive forms

Reactive forms can drive the invalid state directly, so the trigger styling and error text stay synchronized with Angular validation.

A release channel is required.

Disabled state

Disabled triggers preserve the same composition while preventing interaction and dimming the entire control.

Custom Styling

Override select tokens on a local wrapper when a section needs a different trigger radius, a deeper panel shadow, or adjusted item spacing without changing the select composition.

Token Inspector

Inspect the trigger, selected value, icon, panel, labels, items, indicator, separator, and error text with the menu kept open for easier comparison.

Templates

Design Tokens

Use these CSS custom properties to tune trigger sizing, panel behavior, grouped item spacing, indicator placement, and validation feedback without changing the select composition.

SCSS


  --frame-select-trigger-height: 2.5rem;
  --frame-select-trigger-min-width: 12rem;
  --frame-select-trigger-gap: 0.5rem;
  --frame-select-trigger-radius: var(--frame-radius-md);
  --frame-select-trigger-padding-inline: 1rem;
  --frame-select-trigger-font-size: 0.875rem;
  --frame-select-trigger-font-weight: 600;
  --frame-select-trigger-focus-shadow: 0 0 0 3px color-mix(in srgb, var(--frame-ring) 28%, transparent);
  --frame-select-trigger-invalid-border: color-mix(in srgb, var(--frame-destructive) 65%, var(--frame-border));
  --frame-select-trigger-invalid-shadow: 0 0 0 3px color-mix(in srgb, var(--frame-destructive) 14%, transparent);
  --frame-select-trigger-disabled-opacity: 0.55;
  --frame-select-trigger-hover-filter: brightness(0.98);
  --frame-select-trigger-active-filter: brightness(0.96);
  --frame-select-trigger-transition-duration: 150ms;
  --frame-select-content-min-width: 12rem;
  --frame-select-content-max-height: min(18rem, 50vh);
  --frame-select-content-popper-shadow: 0 16px 32px -18px rgb(0 0 0 / 0.28);
  --frame-select-group-gap: 0.125rem;
  --frame-select-item-indicator-size: 1rem;
  --frame-select-item-indicator-offset: 0.625rem;
  --frame-select-item-padding-start: 2rem;
  --frame-select-item-padding-end: 2rem;
  --frame-select-item-padding-inline: 0.75rem;
  --frame-select-item-indicator-font-size: 0.875rem;
  --frame-select-error-color: var(--frame-destructive);
  --frame-select-error-font-size: 0.8125rem;