Skip to content

Repository files navigation

@swiss-ui/angular

Angular components for the Swiss UI design system.

Installation

npm install @swiss-ui/angular

Architecture

Every component ships in two modes:

  • Headless — logic, state, accessibility, ARIA. No classes by default.
  • Styled — wraps headless, adds swiss-* classes from @swiss-ui/core.
// Headless (zero runtime styles)import{SwissButtonComponent}from'@swiss-ui/angular';// With swiss-* classesimport{StyledButtonComponent}from'@swiss-ui/angular/styled';

Bootstrap (standalone)

import{bootstrapApplication}from'@angular/platform-browser';import{provideAnimations}from'@angular/platform-browser/animations';import{AppComponent}from'./app/app.component';bootstrapApplication(AppComponent,{providers: [provideAnimations()],});

Components

Primitives

SwissBoxComponent

Polymorphic container element.

InputTypeDefaultDescription
elBoxElement'div'HTML tag to render
rolestring | nullnullARIA role
ariaLabelstring | nullnullARIA label
<swiss-boxel="section" role="region" ariaLabel="Main content">
Content
</swiss-box>

Layout

SwissContainerComponent

InputTypeDefault
size'sm' | 'md' | 'lg' | 'xl' | 'full''lg'
<swiss-containersize="xl">
Page content
</swiss-container>

SwissStackComponent

InputTypeDefault
direction'row' | 'column' | 'row-reverse' | 'column-reverse''row'
align'start' | 'center' | 'end' | 'stretch' | 'baseline''start'
justify'start' | 'center' | 'end' | 'between' | 'around' | 'evenly''start'
gapstring'0'
wrapbooleanfalse
<swiss-stackdirection="column" gap="1rem" align="center"><swiss-button>One</swiss-button><swiss-button>Two</swiss-button></swiss-stack>

SwissGridComponent / SwissGridItemComponent

InputTypeDefault
colsnumber12
gapstring'0'
align'start' | 'center' | 'end' | 'stretch''stretch'

SwissGridItemComponent:

InputTypeDefault
colSpannumber | nullnull
rowSpannumber | nullnull
<swiss-gridcols="3" gap="1rem"><swiss-grid-itemcolSpan="2">Wide cell</swiss-grid-item><swiss-grid-item>Narrow cell</swiss-grid-item></swiss-grid>

Typography

SwissHeadingComponent

InputTypeDefault
level1 | 2 | 3 | 4 | 5 | 61
size'xs' | 'sm' | 'md' | 'lg' | 'xl' | '2xl' | '3xl' | '4xl' | nullnull
weight'normal' | 'medium' | 'semibold' | 'bold' | nullnull
align'left' | 'center' | 'right' | 'justify' | nullnull
<swiss-headinglevel="2" size="xl" weight="bold">Page Title</swiss-heading>

SwissTextComponent

InputTypeDefault
el'p' | 'span' | 'label' | 'small' | 'strong' | 'em' | 'div''p'
size'xs' | 'sm' | 'md' | 'lg' | 'xl' | nullnull
weight'normal' | 'medium' | 'semibold' | 'bold' | nullnull
alignTextAlign | nullnull
color'default' | 'muted' | 'subtle' | 'disabled' | 'primary' | 'success' | 'warning' | 'error' | nullnull
truncatebooleanfalse
<swiss-textsize="sm" color="muted" truncate>Long text that will be truncated</swiss-text>

Controls

SwissButtonComponent

InputTypeDefault
el'button' | 'a''button'
variant'solid' | 'outline' | 'ghost' | 'link''solid'
size'sm' | 'md' | 'lg''md'
loadingbooleanfalse
disabledbooleanfalse
type'button' | 'submit' | 'reset''button'
hrefstring | nullnull

Content projection:

  • [swissButtonLeftIcon] — icon before label
  • [swissButtonRightIcon] — icon after label
<swiss-buttonvariant="outline" size="lg"><spanswissButtonLeftIcon>+</span>
Add item
</swiss-button>

SwissInputComponent

Implements ControlValueAccessor. Compatible with FormControl and ngModel.

InputTypeDefault
typeInputType'text'
size'sm' | 'md' | 'lg''md'
disabledbooleanfalse
invalidbooleanfalse
placeholderstring''
describedBystring | nullnull
ariaLabelstring | nullnull
ModelTypeDescription
valuestringTwo-way binding via model()

Content projection:

  • [swissInputLeftAddon]
  • [swissInputRightAddon]

Reactive Forms:

<swiss-input[formControl]="emailControl" type="email" placeholder="Email" />

Template-driven:

<swiss-input[(ngModel)]="email" type="email" />

SwissTextareaComponent

InputTypeDefault
resize'none' | 'both' | 'horizontal' | 'vertical''vertical'
rowsnumber3
autoResizebooleanfalse
disabledbooleanfalse
invalidbooleanfalse
placeholderstring''

SwissSelectComponent

InputTypeDefault
size'sm' | 'md' | 'lg''md'
disabledbooleanfalse
invalidbooleanfalse
<swiss-select[formControl]="countryControl"><optionvalue="ch">Switzerland</option><optionvalue="de">Germany</option></swiss-select>

SwissCheckboxComponent

InputTypeDefault
indeterminatebooleanfalse
disabledbooleanfalse
invalidbooleanfalse
valuestring''
namestring | nullnull
ModelType
checkedboolean
<swiss-checkbox[formControl]="agreeControl">I agree to the terms</swiss-checkbox>

SwissRadioGroupComponent + SwissRadioComponent

SwissRadioGroupComponent implements ControlValueAccessor. Child SwissRadioComponent instances connect via SWISS_RADIO_GROUP_TOKEN.

Input (group)TypeDefault
orientation'horizontal' | 'vertical''vertical'
disabledbooleanfalse
namestringauto-generated
Input (radio)TypeRequired
valueunknownyes
disabledbooleanno
<swiss-radio-group[formControl]="sizeControl" orientation="horizontal"><swiss-radiovalue="sm">Small</swiss-radio><swiss-radiovalue="md">Medium</swiss-radio><swiss-radiovalue="lg">Large</swiss-radio></swiss-radio-group>

SwissSwitchComponent

InputTypeDefault
disabledbooleanfalse
ariaLabelstring | nullnull
describedBystring | nullnull
ModelType
checkedboolean
<swiss-switch[formControl]="darkModeControl" ariaLabel="Enable dark mode" />

Feedback

SwissBadgeComponent

InputTypeDefault
variant'solid' | 'outline' | 'subtle''subtle'
color'default' | 'primary' | 'success' | 'warning' | 'error' | 'info''default'
<swiss-badgevariant="solid" color="success">Active</swiss-badge>

SwissAlertComponent

InputTypeDefault
variant'info' | 'success' | 'warning' | 'error''info'

Content projection:

  • [swissAlertIcon]
  • [swissAlertTitle]
  • [swissAlertDescription]
  • [swissAlertActions]
<swiss-alertvariant="warning"><spanswissAlertTitle>Warning</span><spanswissAlertDescription>Please review before proceeding.</span></swiss-alert>

SwissSpinnerComponent

InputTypeDefault
size'xs' | 'sm' | 'md' | 'lg' | 'xl''md'
labelstring'Loading...'
colorstring | nullnull

Respects prefers-reduced-motion.

<swiss-spinnersize="lg" label="Saving changes" />

Overlay

SwissModalComponent

ModelTypeDescription
openbooleanControls visibility
InputTypeDefault
closeOnEscapebooleantrue
closeOnBackdropbooleantrue

Composed via InjectionToken — child components (swiss-modal-header, swiss-modal-body, etc.) connect automatically.

Composed parts:

  • SwissModalOverlayComponent (swiss-modal-overlay)
  • SwissModalContentComponent (swiss-modal-content)
  • SwissModalHeaderComponent (swiss-modal-header)
  • SwissModalBodyComponent (swiss-modal-body)
  • SwissModalFooterComponent (swiss-modal-footer)
  • SwissModalCloseButtonComponent (swiss-modal-close-button)
<button(click)="isOpen.set(true)">Open</button><swiss-modal[(open)]="isOpen"><swiss-modal-header><swiss-headinglevel="2">Confirm action</swiss-heading><swiss-modal-close-button>x</swiss-modal-close-button></swiss-modal-header><swiss-modal-body><swiss-text>Are you sure you want to continue?</swiss-text></swiss-modal-body><swiss-modal-footer><swiss-buttonvariant="ghost" (click)="isOpen.set(false)">Cancel</swiss-button><swiss-buttonvariant="solid">Confirm</swiss-button></swiss-modal-footer></swiss-modal>

SwissTooltipComponent

ModelType
openboolean
InputTypeDefault
placementPlacement'bottom'
delaynumber200
disabledbooleanfalse

Content projection:

  • [swissTooltipTrigger]
  • [swissTooltipContent]
<swiss-tooltipplacement="top"><buttonswissTooltipTrigger>Hover me</button><spanswissTooltipContent>Helpful information</span></swiss-tooltip>

SwissDropdownComponent

ModelType
openboolean

Keyboard: ArrowDown, ArrowUp, Home, End, Escape.

Composed parts:

  • SwissDropdownTriggerComponent (swiss-dropdown-trigger)
  • SwissDropdownContentComponent (swiss-dropdown-content)
  • SwissDropdownItemComponent (swiss-dropdown-item)
  • SwissDropdownSeparatorComponent (swiss-dropdown-separator)
  • SwissDropdownLabelComponent (swiss-dropdown-label)
<swiss-dropdown><swiss-dropdown-trigger>Actions</swiss-dropdown-trigger><swiss-dropdown-content><swiss-dropdown-label>File</swiss-dropdown-label><swiss-dropdown-item>New</swiss-dropdown-item><swiss-dropdown-item>Open</swiss-dropdown-item><swiss-dropdown-separator/><swiss-dropdown-item>Delete</swiss-dropdown-item></swiss-dropdown-content></swiss-dropdown>

Services

SwissThemeService

import{SwissThemeService}from'@swiss-ui/angular/services';
@Component({ ... })exportclassAppComponent{privatetheme=inject(SwissThemeService);readonlycurrentTheme=this.theme.theme;// Signal<'light' | 'dark'>readonlyisDark=this.theme.isDark;// Signal<boolean>toggleTheme(){this.theme.toggle();}}

SwissOverlayService

import{SwissOverlayService}from'@swiss-ui/angular/services';
@Component({ ... })exportclassAppComponent{privateoverlay=inject(SwissOverlayService);openDialog(){constref=this.overlay.open(MyDialogComponent);// ref.destroy() to close programmatically}}

Directives

import{SwissFocusTrapDirective,SwissClickOutsideDirective,SwissEscapeKeyDirective,SwissAutoPlacementDirective,}from'@swiss-ui/angular/directives';
DirectiveSelectorEvents
SwissFocusTrapDirective[swissFocusTrap]
SwissClickOutsideDirective[swissClickOutside]swissClickOutside
SwissEscapeKeyDirective[swissEscapeKey]swissEscapeKey
SwissAutoPlacementDirective[swissAutoPlacement]placementChange

Forms Integration

Reactive Forms

import{Component,inject}from'@angular/core';import{FormBuilder,Validators,ReactiveFormsModule}from'@angular/forms';import{SwissInputComponent,SwissButtonComponent,SwissCheckboxComponent}from'@swiss-ui/angular';
@Component({standalone: true,imports: [ReactiveFormsModule,SwissInputComponent,SwissButtonComponent,SwissCheckboxComponent],template: ` <form [formGroup]="form" (ngSubmit)="submit()"> <swiss-input formControlName="email" type="email" placeholder="Email" [invalid]="form.controls.email.invalid && form.controls.email.touched" /> <swiss-checkbox formControlName="agree">Accept terms</swiss-checkbox> <swiss-button type="submit" [disabled]="form.invalid">Submit</swiss-button> </form> `,})exportclassSignupComponent{privatefb=inject(FormBuilder);form=this.fb.group({email: ['',[Validators.required,Validators.email]],agree: [false,Validators.requiredTrue],});submit(){if(this.form.valid)console.log(this.form.value);}}

Template-driven Forms

import{Component}from'@angular/core';import{FormsModule}from'@angular/forms';import{SwissInputComponent,SwissSwitchComponent}from'@swiss-ui/angular';
@Component({standalone: true,imports: [FormsModule,SwissInputComponent,SwissSwitchComponent],template: ` <form #f="ngForm" (ngSubmit)="submit(f)"> <swiss-input [(ngModel)]="name" name="name" required placeholder="Name" /> <swiss-switch [(ngModel)]="notifications" name="notifications">Notifications</swiss-switch> <button type="submit">Save</button> </form> `,})exportclassProfileComponent{name='';notifications=true;submit(form: NgForm){if(form.valid)console.log({name: this.name,notifications: this.notifications});}}

Accessibility

All components follow WCAG 2.1 AA:

  • Keyboard navigation on all interactive elements
  • ARIA roles, states, and properties on all components
  • Focus management in Modal (focus trap, restore on close)
  • role="switch" with aria-checked on Switch
  • role="radiogroup" with linked radio inputs
  • aria-invalid, aria-describedby on form controls
  • aria-modal, aria-labelledby, aria-describedby on dialogs
  • Screen-reader-only labels on Spinner
  • prefers-reduced-motion respected in Spinner and Modal animations
  • SSR-safe (no document/window access without isPlatformBrowser)

Entrypoints

Import pathContents
@swiss-ui/angularAll headless components, directives, services, tokens
@swiss-ui/angular/styledStyled variants with swiss-* CSS classes
@swiss-ui/angular/directivesDirectives only
@swiss-ui/angular/servicesServices only

About

Angular components for the Swiss UI design system.

Topics

Resources

Stars

0 stars

Watchers

0 watching

Forks

Contributors