Installation

Install Alixan UI in a Nuxt project. Components are copied directly into your app, so you own the source code and can customize it freely.

Create Project

Start with a Nuxt 4 project. If you already have one, you can skip this step.

Terminal
npx nuxi@latest init my-app
nuxt.config.ts
export default defineNuxtConfig({
  app: {
    rootId: 'root', // Change appId to root.
  },
})

Add Tailwind CSS

Alixan UI components use Tailwind CSS classes and CSS variables for the theme.

Install Tailwind
npm install tailwindcss @tailwindcss/vite -D
app/assets/css/tailwind.css

@import 'tailwindcss';

@custom-variant dark (&:is(.dark *));

@theme {
	/* Set your own font. */
	--font-sans: 'Geist', ui-sans-serif, system-ui, sans-serif;

	/* Used for Text, Button and etc... */
	--text-md: 15px;

	/* Default container */
	--container-container: 1500px;
}

@theme inline {
	--color-background: var(--background);
	--color-foreground: var(--foreground);

	--color-card: var(--card);
	--color-card-foreground: var(--card-foreground);

	--color-popover: var(--popover);
	--color-popover-foreground: var(--popover-foreground);

	--color-primary: var(--primary);
	--color-primary-foreground: var(--primary-foreground);

	--color-secondary: var(--secondary);
	--color-secondary-foreground: var(--secondary-foreground);

	--color-muted: var(--muted);
	--color-muted-foreground: var(--muted-foreground);

	--color-accent: var(--accent);
	--color-accent-foreground: var(--accent-foreground);

	--color-destructive: var(--destructive);
	--color-destructive-foreground: var(--destructive-foreground);

	--color-border: var(--border);
	--color-input: var(--input);
	--color-ring: var(--ring);

	--radius-sm: calc(var(--radius) - 4px);
	--radius-md: calc(var(--radius) - 2px);
	--radius-lg: var(--radius);
	--radius-xl: calc(var(--radius) + 2px);

	--color-sidebar-background: var(--sidebar-background);
	--color-sidebar-foreground: var(--sidebar-foreground);
	--color-sidebar-primary: var(--sidebar-primary);
	--color-sidebar-primary-foreground: var(--sidebar-primary-foreground);
	--color-sidebar-accent: var(--sidebar-accent);
	--color-sidebar-accent-foreground: var(--sidebar-accent-foreground);
	--color-sidebar-border: var(--sidebar-border);
	--color-sidebar-ring: var(--sidebar-ring);
}

:root {
	--radius: 0.65rem;

	--background: oklch(1 0 0);
	--foreground: oklch(0.141 0.005 285.823);

	--card: hsl(0, 0%, 98.5%);
	--card-foreground: oklch(0.141 0.005 285.823);

	--popover: oklch(1 0 0);
	--popover-foreground: oklch(0.141 0.005 285.823);

	--primary: oklch(0.141 0.005 285.823);
	--primary-foreground: oklch(0.985 0 0);

	--secondary: oklch(0.967 0.001 286.375);
	--secondary-foreground: oklch(0.21 0.006 285.885);

	--muted: oklch(0.967 0.001 286.375);
	--muted-foreground: oklch(0.552 0.016 285.938);

	--accent: oklch(0.967 0.001 286.375);
	--accent-foreground: oklch(0.21 0.006 285.885);

	--destructive: oklch(0.577 0.245 27.325);
	--destructive-foreground: oklch(1 0 0);

	--border: oklch(0.92 0.004 286.32);
	--input: oklch(0.92 0.004 286.32);
	--ring: oklch(0.708 0 0);

	--sidebar-background: oklch(0.985 0 0);
	--sidebar-foreground: oklch(0.141 0.005 285.823);
	--sidebar-primary: oklch(0.141 0.005 285.823);
	--sidebar-primary-foreground: oklch(0.985 0 0);
	--sidebar-accent: oklch(92% 0 0);
	--sidebar-accent-foreground: oklch(0.21 0.006 285.885);
	--sidebar-border: oklch(0.92 0.004 286.32);
	--sidebar-ring: oklch(0.708 0 0);
}

.dark {
	--background: oklch(0.141 0.005 285.823);
	--foreground: oklch(0.985 0 0);

	--card: oklch(0.21 0.006 285.885);
	--card-foreground: oklch(0.985 0 0);

	--popover: oklch(0.21 0.006 285.885);
	--popover-foreground: oklch(0.985 0 0);

	--primary: oklch(0.985 0 0);
	--primary-foreground: oklch(0.141 0.005 285.823);

	--secondary: oklch(0.274 0.006 286.033);
	--secondary-foreground: oklch(0.985 0 0);

	--muted: oklch(0.274 0.006 286.033);
	--muted-foreground: oklch(0.705 0.015 286.067);

	--accent: oklch(0.274 0.006 286.033);
	--accent-foreground: oklch(0.985 0 0);

	--destructive: oklch(0.704 0.191 22.216);
	--destructive-foreground: oklch(0.985 0 0);

	--border: oklch(1 0 0 / 10%);
	--input: oklch(1 0 0 / 15%);
	--ring: oklch(0.556 0 0);

	--sidebar-background: oklch(0.21 0.006 285.885);
	--sidebar-foreground: oklch(0.985 0 0);
	--sidebar-primary: oklch(0.985 0 0);
	--sidebar-primary-foreground: oklch(0.141 0.005 285.823);
	--sidebar-accent: oklch(0.274 0.006 286.033);
	--sidebar-accent-foreground: oklch(0.985 0 0);
	--sidebar-border: oklch(1 0 0 / 10%);
	--sidebar-ring: oklch(0.439 0 0);
}

@layer base {
	*,
	::after,
	::before,
	::backdrop,
	::file-selector-button {
		border-color: var(--border);
	}

	* {
		@apply m-0 p-0 box-border scroll-smooth outline-none;
	}

	html,
	body {
		@apply size-full min-h-full bg-background text-foreground overflow-hidden;
	}

	body {
		::-webkit-scrollbar {
			width: 6px;
			height: 6px;
		}

		::-webkit-scrollbar-track {
			border-radius: 10px;
			background: color-mix(in srgb, var(--border) 20%, transparent);
		}

		::-webkit-scrollbar-thumb {
			border-radius: 10px;
			background: color-mix(in srgb, var(--muted-foreground) 20%, transparent);
			cursor: grabbing;
		}

		::-webkit-scrollbar-thumb:hover {
			background: color-mix(in srgb, var(--foreground) 40%, transparent);
		}
	}

	#root {
		@apply h-full min-h-full w-full flex flex-col divide-y overflow-y-auto;
	}

	hr {
		@apply w-full h-px;
	}

	button:not(:disabled),
	[role='button']:not(:disabled) {
		cursor: pointer;
	}
	p,
	span {
		color: inherit;
	}

	select {
		-webkit-appearance: none;
		-moz-appearance: none;
		appearance: none;
		background: transparent;
	}

	input::-webkit-inner-spin-button,
	input::-webkit-outer-spin-button {
		-webkit-appearance: none;
	}

	input[type='search']::-webkit-search-cancel-button {
		@apply hidden;

		-webkit-appearance: none;
		appearance: none;
	}

	input[type='number'] {
		-moz-appearance: textfield;
	}
}

/* Used UI blocks. */
@utility island {
	@apply flex flex-col border overflow-hidden;

	border-radius: clamp(var(--radius-md), 20%, var(--radius-2xl));
}

/* Used for dialog, drawer and overlays. */
@utility backdrop {
	@apply fixed inset-0 bg-black/30 dark:bg-black/80;
}
Keep the backdrop utility in your Tailwind CSS. Dialog, Drawer, Global Loader and mobile overlays use it for a consistent overlay color: fixed inset-0 bg-black/30 dark:bg-black/80.
nuxt.config.ts
import tailwindcss from '@tailwindcss/vite'

export default defineNuxtConfig({
  css: ['~/assets/css/tailwind.css'],
  features: {
    inlineStyles: true,
  },
  vite: {
    plugins: [tailwindcss()],
  },
})

Add i18n

Alixan UI works best with Nuxt i18n because documentation labels, table headings and page meta use translation keys. Set missingWarn to false if your app also passes plain values through $t.

Install Nuxt i18n
npm install @nuxtjs/i18n

Configuration

Use strategy: 'prefix_except_default' to keep the default language without a prefix and prefix all other locales.

For more detailed information, visit the Read the i18n guide for details.

nuxt.config.ts
export default defineNuxtConfig({
  modules: ['@nuxtjs/i18n'],
  i18n: {
    vueI18n: './configs/i18n.config.ts',
    defaultLocale: 'en',
    strategy: 'prefix_except_default',
    detectBrowserLanguage: false,
    locales: [
      { code: 'en', name: 'English', file: 'en.json' },
      { code: 'ru', name: 'Русский', file: 'ru.json' },
      { code: 'kk', name: 'Қазақша', file: 'kk.json' },
    ],
  },
})

Point vueI18n to the external config file and disable i18n warnings in i18n/configs/i18n.config.ts.

i18n/configs/i18n.config.ts
export default {
  // Disable warnings because some UI props intentionally pass plain strings through $t.
  missingWarn: false,
  fallbackWarn: false,
}

Install Component

Run the CLI from your project root. The component and required utilities will be copied into your project.

npx alixan-ui-nuxt add button
Created files: components/ui/button/Button.vue , utils/cn.ts . Nuxt auto-import handles the component after it is copied.

Use Component

Use the component directly in your Vue templates. No package import is required for the component itself.

<template>
  <Button color="primary">
    Save changes
  </Button>
</template>