Basic select
Use a trigger, placeholder, and a small item list when the selected value should always be one choice from a fixed set.
Icons by @ng-icons/tabler-icons
The component library does not provide icons.
Button-triggered listbox selection built with composable primitives.
Preview
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>Use a trigger, placeholder, and a small item list when the selected value should always be one choice from a fixed set.
Use labels and separators when the option list has meaningful categories that help people scan faster.
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 triggers preserve the same composition while preventing interaction and dimming the entire control.
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.
Inspect the trigger, selected value, icon, panel, labels, items, indicator, separator, and error text with the menu kept open for easier comparison.
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;