Skip to content

Getting started โ€‹

Mono UI is a small set of Lit-powered web components โ€” <mono-button>, <mono-input>, <mono-card>, etc. They're standard custom elements, so they work in Vue, React, Svelte, plain HTML, or anywhere custom elements run.

Install โ€‹

Place below code inside your package.json after that just install with pnpm i. You'll need a GitHub token, lit is a peer dependency โ€” it ships separately so you can share one copy across the components and your own code.

json
{
    "devDependencies": {
        "lit": "^3.3.2",
        "mono-helper": "git+https://<GITHUB_CLASSIC_TOKEN>@github.com/EJI-ICT/libs#path:/packages/mono-helper"
    }
}

Load the styles โ€‹

Import the global stylesheet once, near the top of your app entry. It registers the CSS variables every component reads (colors, spacing, density, light/dark) and the scoped rules that paint each element.

ts
import 'mono-helper/ui/index.css'

Tree-shakeable imports โ€‹

Each component lives at its own subpath. Import only the ones you use โ€” the rest never enter your bundle.

ts
import 'mono-helper/ui/button'
import 'mono-helper/ui/input'
import 'mono-helper/ui/card'

Importing the module is enough โ€” the file calls customElements.define('mono-โ€ฆ', โ€ฆ) as a side effect, and the tag becomes available everywhere.

Use in Vue โ€‹

Two pieces of one-time setup are needed in vite.config.ts (or your VitePress config). Tell Vue to skip its component resolver for mono-* tags so it leaves them alone for the browser to handle:

ts
import vue from '@vitejs/plugin-vue'

export default {
    plugins: [
        vue({
            template: {
                compilerOptions: {
                    isCustomElement: (tag) => tag.startsWith('mono-'),
                },
            },
        }),
    ],
}

Then any component renders just like an HTML element:

vue
<script setup>
import { ref } from 'vue'
import 'mono-helper/ui/input'
import 'mono-helper/ui/button'

const name = ref('')
</script>

<template>
    <mono-input
        label="Name"
        :model-value="name"
        @mno-input="name = $event.detail.modelValue"
    ></mono-input>

    <mono-button color="primary" @click="alert(`Hello ${name}`)">
        Say hi
    </mono-button>
</template>

A few Vue-specific gotchas:

  • v-model on Lit elements silently misses the dispatched events. Bind :model-value and listen for @mno-change / @mno-input instead, reading from $event.detail.modelValue.
  • For object/array props (mono-menu items, mono-select options, cssClass), use the .prop modifier: :items.prop="x". A plain :items="x" only sets a stringified attribute that Lit ignores.
  • For named slots, write <el slot="name">โ€ฆ</el>. Vue's <template #name> shorthand crashes the compiler on custom elements.