v4.0.0-beta.4

Migrating from v3

Learn how to migrate your project to Shuriken UI v4.

Overview

Shuriken UI v4 now uses Tailwind CSS v4. This new version brings a lot of improvements and new features, but also some breaking changes.

We encourage you to first read the Tailwind upgrade: https://tailwindcss.com/docs/upgrade-guide

Tailwind CSS v4

Less JS, more CSS

With the current Tailwind v3 version, the tailwind.config.js file allows you to override Tailwind’s default design tokens. It’s a customization hub where you can add custom utility classes and themes, disable plugins, and more.

js
import { withShurikenUI } from '@shuriken-ui/tailwind'
import colors from 'tailwindcss/colors'

export default withShurikenUI({
  theme: {
    fontFamilly: {
      sans: ['Inter', 'sans-serif'],
      alt: ['Roboto', 'sans-serif'],
    },
    extend: {
      colors: {
        'primary': colors.violet,
        'primary-invert': colors.white,
      },
    },
  },
})

In Tailwind v4, everything is CSS. You’ll only need to import tailwindcss into your main CSS file, and you’re good to go. We decided to follow the same approach in Shuriken UI v4 to integrate seamlessly with the new Tailwind CSS paradigm. Therefore, same result can be obtained by using the following CSS file:

css
@import 'tailwindcss';
@import '@shuriken-ui/nuxt';

@theme {
  --font-sans: Inter, sans-serif;
  --font-alt: Roboto, sans-serif;

  --color-primary-50: var(--color-violet-50);
  --color-primary-100: var(--color-violet-100);
  --color-primary-200: var(--color-violet-200);
  --color-primary-300: var(--color-violet-300);
  --color-primary-400: var(--color-violet-400);
  --color-primary-500: var(--color-violet-500);
  --color-primary-600: var(--color-violet-600);
  --color-primary-700: var(--color-violet-700);
  --color-primary-800: var(--color-violet-800);
  --color-primary-900: var(--color-violet-900);
  --color-primary-950: var(--color-violet-950);
  --color-primary-invert: var(--color-white);
}
Important note

With this new CSS variables approach, Tailwind v4 is able to infer color classes if your variables start with the --color-* prefix. If you create a --color-custom variable, Tailwind will automatically generate classes like bg-custom or text-custom.

You can learn more about theming in Shuriken UI by reading the theming documentation overview.

Breaking changes

All the improvements brought by the new version of Shuriken UI come with a cost. We had to make some breaking changes to the library to align with the new Tailwind CSS v4, as well as rewrite most components from the ground using Reka UI. Here are the most important changes you need to be aware of.

Nuxt module

We used to serve Shuriken UI as a Nuxt layer. However, for performance and usability reasons, we chose to serve shuriken UI as a standard Nuxt module. This means that you will have to install Shuriken UI as a Nuxt module in your project. You will also need to import your main CSS file in your nuxt.config.js file:

js
export default defineNuxtConfig({
  extends: [
    '@shuriken-ui/nuxt',   ],
  modules: [
    '@shuriken-ui/nuxt',   ],
  css: [
    '~/assets/css/main.css',   ],
})

Utility CSS

We completely removed @shuriken-ui/tailwind package. We switched to pure Tailwind utility classes used in component templates, greatly improving performance, tree shaking, final bundle size and the overall developer experience.

Therefore, utils has been cleaned to only keep nui-focus, nui-mask, nui-slimscroll

Important note

nui-focus now need to have a state prefix like focus:, focus-within:, focus-visible: to work.

From Headless UI to Reka UI

We've been eyeing Radix UI for a while, and we were impressed by the way they were building their components as primitives focusing on accessibility first. Unfortunately, Radix is for React, and there were no Vue alternatives, besides some discontinued projects.

It was until we stumbled upon Reka UI (formerly Radix Vue), a Vue port of Radix UI, that we decided to give it a try. The results exceeded our expectations, and we decided to use Reka UI as the foundation of our component system.

Reka UI

We rewrote all the components using Reka UI, a new library that acts as the Vue port of Radix UI. Reka UI is a library that allows you to create UI components using their unstyled and accessible primitives.

These low level components are extremely well implemented and accessible by default. We used them as the foundation of the Shuriken UI component system. This means that all the components have been rewritten from scratch, and some of them have been removed or renamed.

Important note

You should be familiar with Reka UI and its concepts before using Shuriken UI. If not, you should go through the Reka UI documentation.

Anatomy changes

In previous version, we relied on complex components with hard to understand structure.

vue
<template>
  <BaseAccordion
    :classes="{
      wrapper: 'bg-white rounded-lg shadow-sm',
      summary: 'p-4',
    }"
    :items
  />
</template>

<script setup lang="ts">
const items = [
  {
    title: 'Accordion Item 1',
    content:
      'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quo tandem modo? Inde igitur, inquit, ordiendum est. Primum quid tu dicis breve? Duo Reges: constructio interrete.',
  },
  {
    title: 'Accordion Item 2',
    content:
      'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quo tandem modo? Inde igitur, inquit, ordiendum est. Primum quid tu dicis breve? Duo Reges: constructio interrete.',
  },
  {
    title: 'Accordion Item 3',
    content:
      'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quo tandem modo? Inde igitur, inquit, ordiendum est. Primum quid tu dicis breve? Duo Reges: constructio interrete.',
  },
]
</script>

Building on top of Reka made us switch the way we build components. Rather than building monolithic components, we decided to split them into smaller parts, making them more flexible and easier to maintain.

This approach reduce the nodes in the component tree, reducing the need of using classes property to style the components. In addition, most components inherit from Reka UI components, making them more accessible and easier to use.

Here is how the BaseAccordion component is used in the new version:

vue
<template>
  <BaseAccordion class="bg-white rounded-lg shadow-sm">
    <BaseAccordionItem
      v-for="item in items"
      :key="item.value"
      :value="item.value"
      class="p-4"
    >
      <template #title>
        {{ item.title }}
      </template>

      {{ item.content }}
    </BaseAccordionItem>
  </BaseAccordion>
</template>

<script setup lang="ts">
const items = [
  {
    value: 'accordion-item-1',
    title: 'Accordion Item 1',
    content:
      'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quo tandem modo? Inde igitur, inquit, ordiendum est. Primum quid tu dicis breve? Duo Reges: constructio interrete.',
  },
  {
    value: 'accordion-item-2',
    title: 'Accordion Item 2',
    content:
      'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quo tandem modo? Inde igitur, inquit, ordiendum est. Primum quid tu dicis breve? Duo Reges: constructio interrete.',
  },
  {
    value: 'accordion-item-3',
    title: 'Accordion Item 3',
    content:
      'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Quo tandem modo? Inde igitur, inquit, ordiendum est. Primum quid tu dicis breve? Duo Reges: constructio interrete.',
  },
]
</script>

Inputs changes

In previous version, inputs components such as BaseInput, BaseInputNumber, BaseTextarea, BaseSelect, ... were complex components with a lot of props and options to render labels, errors, etc...

vue
<script setup lang="ts">
const value = ref('')
</script>

<template>
  <BaseInput
    v-model="value"
    type="text"
    label="Search records..."
    placeholder="Type something..."
    rounded="md"
    loading
    icon="lucide:search"
  />
</template>

We decided to simplify them to render only the input itself, and let the developer handle the rest.

We created BaseProviderField components that are used to wrap the input and its label, error, etc... This allow to create higher components such as BaseField:

Search records...
vue
<script setup lang="ts">
const value = ref('')
</script>

<template>
  <BaseField
    v-slot="{ inputRef, inputAttrs }"
    label="Search records..."
    state="loading"
  >
    <div class="focus-within:nui-focus rounded-md flex *:rounded-none *:border-e-0 *:first:rounded-s-md *:last:border-e *:last:rounded-e-md">
      <BaseIconBox size="sm">
        <Icon name="lucide:search" />
      </BaseIconBox>
      <BaseInput
        :ref="inputRef"
        v-model="value"
        v-bind="inputAttrs"
        placeholder="Type something..."
        class="focus-visible:ring-0 border-s-0 ps-0"
      />
    </div>
  </BaseField>
</template>

Model Value changes

  • BaseCheckbox does not accept arrays as model value anymore. You should wrap them in a BaseCheckboxGroup component.
  • BaseRadio does not accept model value anymore. You should wrap them in a BaseRadioGroup component.

Props changes

We made some changes to the props of the components to make them more consistent and easier to use. We also removed some props that were not necessary anymore.

We worked to make styling options and props more consistent, and removed a lot of them, while giving you more control over the components.

  • All the color props have been renamed to variant.
  • Former variant props have been removed.
  • All the contrast props have been removed in favor of css variables.
  • All the shadow props have been removed in favor of tailwind classes.

Removed components

We removed some components that were not necessary anymore, or that were not used by the community. Here is the list of the removed components:

  • ButtonGroup
  • ButtonIcon
  • ButtonAction
  • ButtonClose
  • CheckboxAnimated
  • FocusLoop
  • HeadlessCheckbox
  • HeadlessRadio
  • TabSlider
  • Listbox
  • TreeSelect

Reworked components

At some point, all the components have been reworked to be more consistent and easier to use. However, there are some components that received significant changes, and now have a very different structure. Make sure to check how they work now:

  • Button
  • Autocomplete
  • Select
  • InputNumber
  • Input
  • InputFile
  • Textarea
  • Progress
  • ProgressCircle
  • Tabs

Changelog

For more details about all the changes, you can review the v4 changelog.