Skip to content

UIKit

The UIKit is a library of 38 reusable Vue 3 components in shared/src/uikit/. Each component has its own directory with a consistent file structure and is documented in Storybook. Components are imported via the @uikit/ alias from both frontend/ and admin/.

Running Storybook

bash
pnpm storybook  # Opens at http://localhost:6006

Component List

Form Controls

ComponentImportDescription
GisButton@uikit/button/button.vuePrimary action button with variants and sizes
GisIconButton@uikit/icon-button/icon-button.vueIcon-only button
GisInput@uikit/input/input.vueText input field
GisPasswordInput@uikit/password-input/password-input.vuePassword input with toggle visibility
GisPasswordRequirements@uikit/password-requirements/password-requirements.vueLive password-strength checklist
GisTextarea@uikit/textarea/textarea.vueMulti-line text input
GisSelect@uikit/select/select.vueDropdown select
GisCheckbox@uikit/checkbox/checkbox.vueCheckbox control
GisRadio@uikit/radio/radio.vueRadio button
GisToggle@uikit/toggle/toggle.vueToggle switch
GisSlider@uikit/slider/slider.vueRange slider
GisField@uikit/field/field.vueForm field wrapper (label + error)

Data Display

ComponentImportDescription
GisTable@uikit/table/table.vueData table
GisList@uikit/list/list.vueOrdered/unordered list
GisAvatar@uikit/avatar/avatar.vueUser avatar
GisBadge@uikit/badge/badge.vueStatus badge
GisTag@uikit/tag/tag.vueKeyword tag
GisChip@uikit/chip/chip.vueCloseable chip
GisProgressBar@uikit/progress-bar/progress-bar.vueLinear progress
GisSkeleton@uikit/skeleton/skeleton.vueContent placeholder
GisEmptyState@uikit/empty-state/empty-state.vueNo data state
GisDivider@uikit/divider/divider.vueVisual separator
GisIcon@uikit/icon/icon.vueRemix Icon wrapper (globally registered)

Layout & Containers

ComponentImportDescription
GisCard@uikit/card/card.vueContainer box
GisAccordion@uikit/accordion/accordion.vueCollapsible sections
GisTabs / GisTab@uikit/tabs/tabs.vueTabbed content
GisBreadcrumb@uikit/breadcrumb/breadcrumb.vueNavigation breadcrumb
GisStepper@uikit/stepper/stepper.vueMulti-step indicator
GisPagination@uikit/pagination/pagination.vuePage navigation

Overlays & Feedback

ComponentImportDescription
GisModal@uikit/modal/modal.vueDialog overlay
GisDrawer@uikit/drawer/drawer.vueSide drawer panel
GisDropdown@uikit/dropdown/dropdown.vueDropdown menu
GisPopover@uikit/popover/popover.vueFloating popover
GisTooltip@uikit/tooltip/tooltip.vueTooltip overlay
GisAlert@uikit/alert/alert.vueAlert message box
GisSnackbar@uikit/snackbar/snackbar.vueToast notification
GisSpinner@uikit/spinner/spinner.vueLoading spinner

Special

ComponentImportDescription
GisCookieBanner@uikit/cookie-banner/cookie-banner.vueGDPR cookie consent

File Structure

Each component follows this structure:

shared/src/uikit/<name>/
├── <name>.vue                 # Component
├── <name>.scss                # Styles (BEM + @apply)
├── <name>.stories.ts          # Storybook stories (CSF)
└── types/
    └── <name>.types.ts        # TypeScript types

Usage

Import components via the @uikit/ alias:

vue
<script setup lang="ts">
import GisButton from '@uikit/button/button.vue';
import GisDrawer from '@uikit/drawer/drawer.vue';
</script>

<template>
  <GisButton variant="primary" size="medium" label="Click me" />
  <GisDrawer :open="isOpen" @close="isOpen = false">
    <p>Drawer content</p>
  </GisDrawer>
</template>

GisIcon is the only globally registered component — it does not need an import:

vue
<template>
  <GisIcon name="ri-map-pin-line" />
</template>

Variant System

Components use const arrays for type-safe variants:

typescript
// types/button.types.ts
export const buttonVariants = [
  'primary', 'secondary', 'success', 'info',
  'warning', 'danger', 'contrast', 'ghost',
] as const;
export const buttonSizes = ['small', 'medium', 'large'] as const;

export type ButtonVariant = (typeof buttonVariants)[number];
export type ButtonSize = (typeof buttonSizes)[number];

Styling Pattern

All component styles follow BEM naming with SCSS nesting:

scss
.gis-button {
  @apply inline-flex items-center justify-center gap-2 font-normal text-sm;
  border-radius: 12px;

  &--primary {
    @apply bg-primary-600 text-white;
    &:hover:not(:disabled) {
      @apply bg-primary-700;
    }
    &:active:not(:disabled) {
      @apply bg-primary-800;
    }
  }

  &--small { @apply text-xs h-8 px-3; }
  &--medium { @apply text-sm h-10 px-4; }
  &--large { @apply text-base h-12 px-5; }

  &:disabled { @apply opacity-50 cursor-not-allowed; }

  &__icon { @apply text-current; }
  &__label { @apply leading-none; }
}

All SCSS files are imported in shared/src/uikit/index.scss.

Storybook Stories

Stories use Component Story Format (CSF):

typescript
import GisButton from './button.vue';
import { buttonVariants, buttonSizes } from './types/button.types';

export default {
  title: 'UIKit/Button',
  component: GisButton,
  tags: ['autodocs'],
  argTypes: {
    variant: { control: 'select', options: buttonVariants },
    size: { control: 'select', options: buttonSizes },
  },
};

export const Primary = {
  args: { label: 'Button', variant: 'primary', size: 'medium' },
};

export const AllVariants = {
  render: () => ({
    components: { GisButton },
    template: `
      <div class="flex gap-3">
        <GisButton v-for="v in variants" :key="v" :variant="v" :label="v" />
      </div>
    `,
    setup: () => ({ variants: buttonVariants }),
  }),
};

Rules:

  • Title pattern: UIKit/<ComponentName>
  • Always include tags: ['autodocs']
  • Use const arrays from types for argType options
  • Include individual variant stories + an AllVariants gallery