Using with other UI libraries

ZUI is a CSS-first library. The React and Astro components are thin wrappers around CSS classes, so ZUI works with any UI library or framework — Vue, Svelte, Solid, Angular, Web Components, or plain HTML.

Setup

Install ZUI and import the stylesheet however your framework handles CSS:

npm install @mrmartineau/zui
@import '@mrmartineau/zui/css';

Or use the minified version:

@import '@mrmartineau/zui/css/min';

Or link directly in HTML:

<link rel="stylesheet" href="node_modules/@mrmartineau/zui/dist/css/zui.css" />

CSS class reference

All ZUI components use BEM-style classes prefixed with zui-. Apply them directly in any template language.

Button

ClassPurpose
zui-buttonBase button styles
zui-button-variant-fillSolid filled button (default)
zui-button-variant-subtleSubtle background
zui-button-variant-outlineOutlined border
zui-button-variant-ghostNo background until hover
zui-button-variant-linkLooks like a text link
zui-button-color-themeTheme colour (default)
zui-button-color-accentAccent colour
zui-button-color-destructiveDestructive/danger colour
zui-button-size-xsExtra small
zui-button-size-smSmall
zui-button-size-lgLarge
zui-button-size-xlExtra large
zui-button-shape-hardSharp corners
zui-button-shape-softExtra rounded corners
zui-button-shape-squircleSquircle shape
zui-button-iconIcon-only button (square)

Badge

ClassPurpose
zui-badgeBase badge styles
zui-badge-variant-fillSolid filled badge
zui-badge-variant-outlineOutlined badge
zui-badge-color-{color}Color variant (red, green, blue, violet, amber, orange, yellow, lime, emerald, teal, cyan, sky, indigo, purple, fuchsia, pink, rose, gray)

Card

ClassPurpose
zui-cardBase card container
zui-card-interactiveAdds hover/focus styles for clickable cards
zui-card-headerCard header section
zui-card-titleCard title text
zui-card-descriptionCard description text
zui-card-bodyCard body content

Form elements

ClassPurpose
zui-inputText input
zui-textareaTextarea
zui-selectSelect dropdown
zui-labelForm label
zui-checkboxCheckbox
zui-radioRadio button

Framework examples

Vue

<template>
  <button class="zui-button zui-button-variant-fill zui-button-color-theme">
    Save
  </button>
  <span class="zui-badge zui-badge-color-green">Active</span>

  <div class="zui-card">
    <div class="zui-card-header">
      <h2 class="zui-card-title">{{ title }}</h2>
      <p class="zui-card-description">{{ description }}</p>
    </div>
    <div class="zui-card-body">
      <input class="zui-input" v-model="email" type="email" placeholder="Email" />
      <button class="zui-button zui-button-variant-outline" @click="submit">
        Submit
      </button>
    </div>
  </div>
</template>

Svelte

<button class="zui-button zui-button-variant-fill zui-button-color-theme">
  Save
</button>
<span class="zui-badge zui-badge-color-green">Active</span>

<div class="zui-card">
  <div class="zui-card-header">
    <h2 class="zui-card-title">{title}</h2>
    <p class="zui-card-description">{description}</p>
  </div>
  <div class="zui-card-body">
    <input class="zui-input" bind:value={email} type="email" placeholder="Email" />
    <button class="zui-button zui-button-variant-outline" on:click={submit}>
      Submit
    </button>
  </div>
</div>

Solid

function App() {
  return (
    <>
      <button class="zui-button zui-button-variant-fill zui-button-color-theme">
        Save
      </button>
      <span class="zui-badge zui-badge-color-green">Active</span>

      <div class="zui-card">
        <div class="zui-card-header">
          <h2 class="zui-card-title">{title()}</h2>
        </div>
        <div class="zui-card-body">
          <input class="zui-input" type="email" placeholder="Email" />
        </div>
      </div>
    </>
  )
}

Plain HTML

<button class="zui-button zui-button-variant-fill zui-button-color-theme">
  Save
</button>

<span class="zui-badge zui-badge-color-green">Active</span>

<div class="zui-card">
  <div class="zui-card-header">
    <h2 class="zui-card-title">Card title</h2>
    <p class="zui-card-description">Card description</p>
  </div>
  <div class="zui-card-body">
    <label class="zui-label" for="email">Email</label>
    <input class="zui-input" id="email" type="email" />
    <button class="zui-button zui-button-variant-outline">Submit</button>
  </div>
</div>

Utility classes

ZUI also ships utility classes you can use in any framework:

<!-- Spacing -->
<div class="zui-mt-md zui-px-lg">...</div>

<!-- Flex -->
<div class="zui-flex zui-flex-row zui-gap-md">...</div>

<!-- Flow (vertical rhythm) -->
<div class="zui-flow">...</div>

<!-- Prose (long-form content) -->
<div class="zui-prose">...</div>

Building wrapper components

You can create thin wrapper components in any framework using the CSS classes. Here's a Vue example:

<!-- ZButton.vue -->
<template>
  <component
    :is="href ? 'a' : 'button'"
    :href="href"
    :class="classes"
  >
    <slot />
  </component>
</template>

<script setup>
import { computed } from 'vue'

const props = defineProps({
  variant: { type: String, default: 'fill' },
  accent: { type: String, default: 'primary' },
  size: { type: String, default: null },
  href: { type: String, default: null },
})

const classes = computed(() => [
  'zui-button',
  `zui-button-variant-${props.variant}`,
  `zui-button-color-${props.accent}`,
  props.size && `zui-button-size-${props.size}`,
].filter(Boolean))
</script>

CSS Layers

ZUI uses CSS @layer to organize styles. Your unlayered styles will always take precedence, making overrides straightforward in any framework:

/* Your framework's styles — always wins over ZUI layers */
.my-custom-button {
  background-color: hotpink;
}

Theming

Theming is framework-agnostic — just override CSS custom properties. See the Theming page for full details.

:root {
  --color-theme: light-dark(var(--color-violet-600), var(--color-violet-400));
  --radius-scale: 1.5;
}

Theme

Copy this CSS to your project's theme.css file: