@swiss-ui/vue
v1.0.0
Published
Vue 3 components for Swiss UI design system
Readme
@swiss-ui/vue
Vue 3 components for the Swiss UI design system.
Installation
npm install @swiss-ui/vue vueArchitecture
Every component ships in two modes:
- Headless (
@swiss-ui/vue) — logic, state, ARIA, no CSS classes - Styled (
@swiss-ui/vue/styled) — headless wrappers withswiss-*classes from@swiss-ui/core
Usage
Headless (default)
<script setup lang="ts">
import { SwissButton, SwissInput } from '@swiss-ui/vue'
</script>
<template>
<SwissButton variant="solid" size="md">Submit</SwissButton>
<SwissInput v-model="value" placeholder="Enter text" />
</template>Styled
<script setup lang="ts">
import { SwissButton } from '@swiss-ui/vue/styled'
</script>
<template>
<SwissButton variant="solid">Styled Button</SwissButton>
</template>Global registration via plugin
import { createApp } from 'vue'
import { createSwissUI } from '@swiss-ui/vue/plugin'
import App from './App.vue'
const app = createApp(App)
app.use(createSwissUI({ prefix: 'Swiss' }))
app.mount('#app')Plugin options:
| Option | Type | Default | Description |
|-----------|-----------|-----------|-----------------------------------------------|
| prefix | string | 'Swiss' | Component name prefix. Pass '' to remove it |
| styled | boolean | false | Register styled variants |
Components
Primitives
| Component | Props | Slots |
|--------------------|------------------------------------|----------------|
| SwissBox | is | default |
| SwissTextPrimitive | is, size, weight, align, truncate | default |
Layout
| Component | Props | Slots |
|------------------|------------------------------------|--------------------------|
| SwissContainer | size | default |
| SwissStack | gap, align, justify, direction | default |
| SwissGrid | cols, gap, align | default |
| SwissGridItem | colSpan, rowSpan | default |
Typography
| Component | Props | Slots |
|----------------|-------------------------------------------|-----------|
| SwissHeading | is, level, size, weight, align | default |
| SwissText | is, size, weight, align, color, truncate | default |
Controls
| Component | Props | Emits | Slots |
|------------------|--------------------------------------------------|-------------------------------|---------------------------------|
| SwissButton | is, variant, size, loading, disabled, type | click | default, leftIcon, rightIcon |
| SwissInput | modelValue, size, disabled, invalid, type, placeholder | update:modelValue, change, blur, focus | leftAddon, rightAddon |
| SwissTextarea | modelValue, resize, rows, autoResize, disabled, invalid | update:modelValue, change, blur, focus | — |
| SwissSelect | modelValue, size, disabled, invalid, placeholder | update:modelValue, change, blur | default (option elements) |
| SwissCheckbox | modelValue, indeterminate, disabled, invalid, label, value | update:modelValue, change | default |
| SwissRadioGroup | modelValue, name, disabled, orientation | update:modelValue, change | default |
| SwissRadio | value, disabled, label | — | default |
| SwissSwitch | modelValue, disabled, label | update:modelValue, change | default |
Feedback
| Component | Props | Slots |
|---------------|------------------------------|-------------------------------------------|
| SwissBadge | variant, color | default |
| SwissAlert | variant | icon, title, description, actions, default |
| SwissSpinner | size, color, label | — |
Overlay
| Component | Props | Emits | Slots |
|-------------------------|------------------------------------------|------------------------|----------------------|
| SwissModal | open | update:open, open, close | default, portal |
| SwissModalOverlay | — | — | default |
| SwissModalContent | — | — | default |
| SwissModalHeader | — | — | default |
| SwissModalBody | — | — | default |
| SwissModalFooter | — | — | default |
| SwissModalCloseButton | — | — | default |
| SwissTooltip | open, placement, delay, disabled, content | update:open | default (trigger), content |
| SwissDropdown | open | update:open, open, close | default, portal |
| SwissDropdownTrigger | — | — | default |
| SwissDropdownContent | — | — | default |
| SwissDropdownItem | disabled, onSelect | select | default |
| SwissDropdownSeparator | — | — | — |
| SwissDropdownLabel | — | — | default |
Examples
Modal
<script setup lang="ts">
import { ref } from 'vue'
import {
SwissModal,
SwissModalOverlay,
SwissModalContent,
SwissModalHeader,
SwissModalBody,
SwissModalFooter,
SwissModalCloseButton,
SwissButton,
} from '@swiss-ui/vue'
const open = ref(false)
</script>
<template>
<SwissButton @click="open = true">Open Modal</SwissButton>
<SwissModal v-model:open="open">
<template #portal>
<SwissModalOverlay>
<SwissModalContent>
<SwissModalHeader>
<h2>Confirm Action</h2>
<SwissModalCloseButton />
</SwissModalHeader>
<SwissModalBody>
<p>Are you sure you want to continue?</p>
</SwissModalBody>
<SwissModalFooter>
<SwissButton variant="ghost" @click="open = false">Cancel</SwissButton>
<SwissButton variant="solid">Confirm</SwissButton>
</SwissModalFooter>
</SwissModalContent>
</SwissModalOverlay>
</template>
</SwissModal>
</template>Dropdown
<script setup lang="ts">
import {
SwissDropdown,
SwissDropdownTrigger,
SwissDropdownContent,
SwissDropdownItem,
SwissDropdownSeparator,
} from '@swiss-ui/vue'
</script>
<template>
<SwissDropdown>
<SwissDropdownTrigger>Actions</SwissDropdownTrigger>
<template #portal>
<SwissDropdownContent>
<SwissDropdownItem @select="handleEdit">Edit</SwissDropdownItem>
<SwissDropdownItem @select="handleDuplicate">Duplicate</SwissDropdownItem>
<SwissDropdownSeparator />
<SwissDropdownItem :disabled="true">Delete</SwissDropdownItem>
</SwissDropdownContent>
</template>
</SwissDropdown>
</template>RadioGroup
<script setup lang="ts">
import { ref } from 'vue'
import { SwissRadioGroup, SwissRadio } from '@swiss-ui/vue'
const size = ref('md')
</script>
<template>
<SwissRadioGroup v-model="size" name="size" orientation="horizontal">
<SwissRadio value="sm" label="Small" />
<SwissRadio value="md" label="Medium" />
<SwissRadio value="lg" label="Large" />
</SwissRadioGroup>
</template>Vue Router integration
SwissButton accepts any component via :is, including RouterLink:
<script setup lang="ts">
import { RouterLink } from 'vue-router'
import { SwissButton } from '@swiss-ui/vue'
</script>
<template>
<SwissButton :is="RouterLink" to="/dashboard" variant="link">
Go to Dashboard
</SwissButton>
</template>Accessibility
All components follow WCAG 2.1 AA:
- Keyboard navigable: Tab, Enter, Space, Arrow keys, Escape, Home, End
- Proper ARIA roles:
dialog,alert,switch,radiogroup,menu,menuitem,tooltip - Focus trap in
SwissModal— focus stays inside the dialog while open SwissSpinnerincludes a visible label viaaria-labeland<title>SwissCheckboxsupportsindeterminatestate viaaria-checked="mixed"SwissAlertusesaria-live="assertive"for errors,"polite"for others
SSR / Nuxt
Overlay components (SwissModal, SwissTooltip, SwissDropdown) use <Teleport to="body"> with an onMounted guard, making them safe for SSR. No document access happens during server-side rendering.
Build
npm run build # compile to dist/
npm run test # run Vitest
npm run typecheck # vue-tsc check