Lightweight Lit components with shadcn-inspired theming, Tailwind CSS v4 styling, and Lucide icons.
View Live Demo & Interactive Documentation → Explore all components with live examples, copy-paste code snippets, and interactive playgrounds.
dark classnpm install lit @mariozechner/mini-lit
npm install -D @tailwindcss/vite
// vite.config.ts
import { defineConfig } from "vite";
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
plugins: [tailwindcss()],
});
npm install -D @tailwindcss/cli
// package.json scripts
"scripts": {
"dev": "tailwindcss -i ./src/app.css -o ./dist/app.css --watch",
"build": "tailwindcss -i ./src/app.css -o ./dist/app.css --minify"
}
/* src/app.css */
/* Import theme (includes dark mode and utilities) */
@import "@mariozechner/mini-lit/styles/themes/default.css";
/* Tell Tailwind to scan mini-lit components */
@source "../node_modules/@mariozechner/mini-lit/dist";
/* Import Tailwind */
@import "tailwindcss";
If you're using LitElement components with decorators (custom elements or your own components extending LitElement), you must configure TypeScript properly:
// tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true,
"useDefineForClassFields": false // Critical for LitElement reactivity!
}
}
Note: useDefineForClassFields: false is essential for LitElement's @property() and @state() decorators to work correctly. Without this setting, reactive properties won't trigger updates properly.
import { html, render } from "lit";
import { Button } from "@mariozechner/mini-lit/dist/Button.js";
import { Card } from "@mariozechner/mini-lit/dist/Card.js";
import { icon } from "@mariozechner/mini-lit/dist/icons.js";
import "@mariozechner/mini-lit/dist/ThemeToggle.js";
import { Send } from "lucide";
import "./app.css";
const App = () => html`
<div class="p-8 bg-background text-foreground min-h-screen">
<!-- mini-lit components with internal state are full LitElement instances with custom tags -->
<theme-toggle class="fixed top-4 right-4"></theme-toggle>
<!-- mini-lit components without internal state are functional components returning TemplateResult -->
${Card(html`
<h1 class="text-2xl font-bold mb-4">Hello mini-lit!</h1>
${Button({
children: html`
${icon(Send, "sm")}
<span>Send Message</span>
`,
})}
`)}
</div>
`;
render(App(), document.body);
Stateless components that return TemplateResult:
import { Button, Card, Badge } from "@mariozechner/mini-lit";
// Use directly in templates
${Button({ variant: "primary", children: "Click me" })}
${Badge({ children: "New" })}
Stateful components that extend LitElement:
// Custom elements are automatically registered when using the main import
import "@mariozechner/mini-lit";
// Use as HTML tags
<theme-toggle></theme-toggle>
<code-block .code=${"console.log('Hello')"} language="javascript"></code-block>
IMPORTANT: The root index (@mariozechner/mini-lit) now only exports core utilities (component system, i18n, and icons). Individual components are not exported from the root to encourage optimal tree-shaking.
// ✅ Optimal - only includes what you use (~50-100KB)
import { Button } from "@mariozechner/mini-lit/dist/Button.js";
import { Card } from "@mariozechner/mini-lit/dist/Card.js";
import { icon } from "@mariozechner/mini-lit/dist/icons.js";
import "@mariozechner/mini-lit/dist/ThemeToggle.js";
// ⚠️ Root index only exports core utilities (NOT components)
import { i18n, setTranslations, createComponent } from "@mariozechner/mini-lit";
What's exported from the root index:
ComponentLitBase, createComponent, defineComponent, styleComponent, and related typesi18n, setTranslations, setLanguage, getCurrentLanguage, defaultEnglish, defaultGermanicon function and related utilitiesAvailable component paths:
/dist/Button.js, /dist/Card.js, /dist/Input.js, /dist/Select.js, /dist/Checkbox.js, etc./dist/ThemeToggle.js, /dist/CodeBlock.js, /dist/MarkdownBlock.js, /dist/LanguageSelector.js, etc./dist/mini.js (fc, createState, refs)Bundle Size:
mini-lit uses shadcn/ui compatible themes with CSS custom properties for colors, borders, and shadows.
default - Clean, modern themeclaude - Claude-inspired themeSwitch themes by importing a different CSS file:
@import "@mariozechner/mini-lit/styles/themes/claude.css";
Toggle dark mode via the dark class:
document.documentElement.classList.toggle("dark");
Or use the built-in <theme-toggle> component.
For custom themes and theme generators:
declare module "@mariozechner/mini-lit" {
interface i18nMessages extends MiniLitRequiredMessages {
Welcome: string;
Settings: string;
cartItems: (count: number) => string;
greeting: (name: string, time: string) => string;
}
}
import { setTranslations, defaultEnglish, defaultGerman } from "@mariozechner/mini-lit";
const translations = {
en: {
...defaultEnglish, // Includes required messages like "Copy", "Copied!"
Welcome: "Welcome",
Settings: "Settings",
cartItems: (count: number) =>
count === 0 ? "Your cart is empty" : count === 1 ? "1 item in your cart" : `${count} items in your cart`,
greeting: (name: string, time: string) => `Good ${time}, ${name}!`,
},
de: {
...defaultGerman, // Includes required messages like "Kopieren", "Kopiert!"
Welcome: "Willkommen",
Settings: "Einstellungen",
cartItems: (count: number) =>
count === 0
? "Ihr Warenkorb ist leer"
: count === 1
? "1 Artikel im Warenkorb"
: `${count} Artikel im Warenkorb`,
greeting: (name: string, time: string) => `Guten ${time}, ${name}!`,
},
};
setTranslations(translations);
import { i18n, getCurrentLanguage, setLanguage } from "@mariozechner/mini-lit";
// Simple strings
${i18n("Welcome")}
${i18n("Settings")}
// Functions with parameters
${i18n("cartItems")(3)} // "3 items in your cart"
${i18n("greeting")("Alice", "morning")} // "Good morning, Alice!"
// Language management
getCurrentLanguage() // "en" or "de"
setLanguage("de") // switches to German, reloads page
// Add language selector to UI
<language-selector></language-selector>
The mini-lit repository includes both the component library and a comprehensive example gallery showcasing all components.
# Clone the repository
git clone https://github.com/badlogic/mini-lit.git
cd mini-lit
# Install dependencies
npm install
Run the development server with hot module replacement:
npm run dev
This command orchestrates:
/src, outputting to /dist)/example), automatically picking up the latest mini-lit buildsOpen the URL displayed by Vite (typically http://localhost:5173) to view the example gallery. Any changes to either the mini-lit source code or the example application will trigger automatic rebuilds and browser updates through HMR.
mini-lit/
├── src/ # mini-lit component library source
├── dist/ # Compiled library output
├── styles/ # Theme CSS files
├── example/ # Interactive component gallery
│ └── src/
│ └── pages/ # Individual component demos
└── package.json # Library package configuration
Run formatting and linting checks for both the library and example:
npm run check
This command:
# Build the library
npm run build
# Build the example gallery
cd example && npm run build
# Build and publish the library to npm
npm run build
npm publish --access public
# Quick sync (when only source files changed)
./run.sh sync
# Full deploy (when Docker/infrastructure changed)
./run.sh deploy
The sync command builds and syncs files without restarting services, while deploy also restarts the Docker containers on the server.
See the /example directory for a complete working example with all components, or visit the live demo.
MIT
TypeScript
96.0%
CSS
2.8%
Lightweight Lit components with shadcn-inspired theming, Tailwind CSS v4 styling, and Lucide icons.
View Live Demo & Interactive Documentation → Explore all components with live examples, copy-paste code snippets, and interactive playgrounds.
dark classnpm install lit @mariozechner/mini-lit
npm install -D @tailwindcss/vite
// vite.config.ts
import { defineConfig } from "vite";
import tailwindcss from "@tailwindcss/vite";
export default defineConfig({
plugins: [tailwindcss()],
});
npm install -D @tailwindcss/cli
// package.json scripts
"scripts": {
"dev": "tailwindcss -i ./src/app.css -o ./dist/app.css --watch",
"build": "tailwindcss -i ./src/app.css -o ./dist/app.css --minify"
}
/* src/app.css */
/* Import theme (includes dark mode and utilities) */
@import "@mariozechner/mini-lit/styles/themes/default.css";
/* Tell Tailwind to scan mini-lit components */
@source "../node_modules/@mariozechner/mini-lit/dist";
/* Import Tailwind */
@import "tailwindcss";
If you're using LitElement components with decorators (custom elements or your own components extending LitElement), you must configure TypeScript properly:
// tsconfig.json
{
"compilerOptions": {
"experimentalDecorators": true,
"useDefineForClassFields": false // Critical for LitElement reactivity!
}
}
Note: useDefineForClassFields: false is essential for LitElement's @property() and @state() decorators to work correctly. Without this setting, reactive properties won't trigger updates properly.
import { html, render } from "lit";
import { Button } from "@mariozechner/mini-lit/dist/Button.js";
import { Card } from "@mariozechner/mini-lit/dist/Card.js";
import { icon } from "@mariozechner/mini-lit/dist/icons.js";
import "@mariozechner/mini-lit/dist/ThemeToggle.js";
import { Send } from "lucide";
import "./app.css";
const App = () => html`
<div class="p-8 bg-background text-foreground min-h-screen">
<!-- mini-lit components with internal state are full LitElement instances with custom tags -->
<theme-toggle class="fixed top-4 right-4"></theme-toggle>
<!-- mini-lit components without internal state are functional components returning TemplateResult -->
${Card(html`
<h1 class="text-2xl font-bold mb-4">Hello mini-lit!</h1>
${Button({
children: html`
${icon(Send, "sm")}
<span>Send Message</span>
`,
})}
`)}
</div>
`;
render(App(), document.body);
Stateless components that return TemplateResult:
import { Button, Card, Badge } from "@mariozechner/mini-lit";
// Use directly in templates
${Button({ variant: "primary", children: "Click me" })}
${Badge({ children: "New" })}
Stateful components that extend LitElement:
// Custom elements are automatically registered when using the main import
import "@mariozechner/mini-lit";
// Use as HTML tags
<theme-toggle></theme-toggle>
<code-block .code=${"console.log('Hello')"} language="javascript"></code-block>
IMPORTANT: The root index (@mariozechner/mini-lit) now only exports core utilities (component system, i18n, and icons). Individual components are not exported from the root to encourage optimal tree-shaking.
// ✅ Optimal - only includes what you use (~50-100KB)
import { Button } from "@mariozechner/mini-lit/dist/Button.js";
import { Card } from "@mariozechner/mini-lit/dist/Card.js";
import { icon } from "@mariozechner/mini-lit/dist/icons.js";
import "@mariozechner/mini-lit/dist/ThemeToggle.js";
// ⚠️ Root index only exports core utilities (NOT components)
import { i18n, setTranslations, createComponent } from "@mariozechner/mini-lit";
What's exported from the root index:
ComponentLitBase, createComponent, defineComponent, styleComponent, and related typesi18n, setTranslations, setLanguage, getCurrentLanguage, defaultEnglish, defaultGermanicon function and related utilitiesAvailable component paths:
/dist/Button.js, /dist/Card.js, /dist/Input.js, /dist/Select.js, /dist/Checkbox.js, etc./dist/ThemeToggle.js, /dist/CodeBlock.js, /dist/MarkdownBlock.js, /dist/LanguageSelector.js, etc./dist/mini.js (fc, createState, refs)Bundle Size:
mini-lit uses shadcn/ui compatible themes with CSS custom properties for colors, borders, and shadows.
default - Clean, modern themeclaude - Claude-inspired themeSwitch themes by importing a different CSS file:
@import "@mariozechner/mini-lit/styles/themes/claude.css";
Toggle dark mode via the dark class:
document.documentElement.classList.toggle("dark");
Or use the built-in <theme-toggle> component.
For custom themes and theme generators:
declare module "@mariozechner/mini-lit" {
interface i18nMessages extends MiniLitRequiredMessages {
Welcome: string;
Settings: string;
cartItems: (count: number) => string;
greeting: (name: string, time: string) => string;
}
}
import { setTranslations, defaultEnglish, defaultGerman } from "@mariozechner/mini-lit";
const translations = {
en: {
...defaultEnglish, // Includes required messages like "Copy", "Copied!"
Welcome: "Welcome",
Settings: "Settings",
cartItems: (count: number) =>
count === 0 ? "Your cart is empty" : count === 1 ? "1 item in your cart" : `${count} items in your cart`,
greeting: (name: string, time: string) => `Good ${time}, ${name}!`,
},
de: {
...defaultGerman, // Includes required messages like "Kopieren", "Kopiert!"
Welcome: "Willkommen",
Settings: "Einstellungen",
cartItems: (count: number) =>
count === 0
? "Ihr Warenkorb ist leer"
: count === 1
? "1 Artikel im Warenkorb"
: `${count} Artikel im Warenkorb`,
greeting: (name: string, time: string) => `Guten ${time}, ${name}!`,
},
};
setTranslations(translations);
import { i18n, getCurrentLanguage, setLanguage } from "@mariozechner/mini-lit";
// Simple strings
${i18n("Welcome")}
${i18n("Settings")}
// Functions with parameters
${i18n("cartItems")(3)} // "3 items in your cart"
${i18n("greeting")("Alice", "morning")} // "Good morning, Alice!"
// Language management
getCurrentLanguage() // "en" or "de"
setLanguage("de") // switches to German, reloads page
// Add language selector to UI
<language-selector></language-selector>
The mini-lit repository includes both the component library and a comprehensive example gallery showcasing all components.
# Clone the repository
git clone https://github.com/badlogic/mini-lit.git
cd mini-lit
# Install dependencies
npm install
Run the development server with hot module replacement:
npm run dev
This command orchestrates:
/src, outputting to /dist)/example), automatically picking up the latest mini-lit buildsOpen the URL displayed by Vite (typically http://localhost:5173) to view the example gallery. Any changes to either the mini-lit source code or the example application will trigger automatic rebuilds and browser updates through HMR.
mini-lit/
├── src/ # mini-lit component library source
├── dist/ # Compiled library output
├── styles/ # Theme CSS files
├── example/ # Interactive component gallery
│ └── src/
│ └── pages/ # Individual component demos
└── package.json # Library package configuration
Run formatting and linting checks for both the library and example:
npm run check
This command:
# Build the library
npm run build
# Build the example gallery
cd example && npm run build
# Build and publish the library to npm
npm run build
npm publish --access public
# Quick sync (when only source files changed)
./run.sh sync
# Full deploy (when Docker/infrastructure changed)
./run.sh deploy
The sync command builds and syncs files without restarting services, while deploy also restarts the Docker containers on the server.
See the /example directory for a complete working example with all components, or visit the live demo.
MIT
TypeScript
96.0%
CSS
2.8%