Web Components library for sharing visual elements and styles across LUKSO projects. Built with Lit + Tailwind CSS v4 + Storybook.
Published as @lukso/web-components on npm.
pnpm dev # Start dev server (Vite + Storybook + CEM analyzer)
pnpm build # Full production build → package/
pnpm test:unit # Run vitest unit tests (src/shared/tools/**/*.spec.ts only)
pnpm test:storybook # Run Storybook interaction tests
pnpm lint # Lint all (eslint, stylelint, prettier, tsc)
pnpm lint:fix # Auto-fix all
pnpm storybook # Start Storybook on port 6006
pnpm analyze # Run Custom Elements Manifest analyzer (cem)src/
components/ # Web components (one dir per component)
lukso-button/
index.ts # Component class + exports
style.css # Component-specific Tailwind styles
*.stories.ts # Storybook stories
index.ts # AUTO-GENERATED — do not edit manually
shared/
tailwind-element/ # Base class (TailwindStyledElement, TailwindElement)
safe-custom-element.ts # SSR-safe @safeCustomElement decorator
tools/ # Utilities (cn, tailwind-config, copy-assets, etc.)
styles/ # SCSS + generated CSS files
generated/ # AUTO-GENERATED by scripts/generate-styles.ts
assets/ # Fonts, images
types.ts # Shared TypeScript types
enums.ts # Shared enums
scripts/
generate-styles.ts # Generates colors/fonts/shadows CSS files
build-component-v4.js # Builds component-v4.css (used by TailwindElement)
build-main-v4.js # Builds main-v4.css
package/ # Build output (published to npm)
dist/ # Component JS/CJS/d.ts files
tools/ # Shared tools output
All components extend TailwindStyledElement(style) mixed with withTheme():
@safeCustomElement('lukso-button')
export class LuksoButton extends withTheme(TailwindStyledElement(style)) {
@property({ type: String }) variant: ButtonVariant = 'primary'
// ...
render() {
return html`...`
}
}
declare global {
interface HTMLElementTagNameMap {
'lukso-button': LuksoButton
}
}- Use
@safeCustomElement(not@customElement) — prevents duplicate registration in SSR/HMR - Use
tv()fromtailwind-variantsfor class composition - Use
cn()from@/shared/toolsfor merging classes - Path alias
@→src/
The build has multiple steps run in sequence (all included in pnpm build):
pnpm config-prepare— runsvite.tools.config.ts(buildspackage/tools/)tsx scripts/generate-styles.ts— generatessrc/shared/styles/generated/*.cssnode scripts/build-component-v4.js— builds component-v4.csspnpm analyze— regeneratespackage/custom-elements.jsonandsrc/components/index.tstsx vite.full.config.ts— builds components intopackage/dist/- SCSS compilation →
package/dist/styles/main.css node scripts/build-main-v4.js— builds main-v4.css
Gotcha: src/components/index.ts is auto-generated by the CEM analyzer step. Never edit it manually. Run pnpm analyze to regenerate.
Gotcha: package/ dir contains published package. The package/package.json is auto-generated from root package.json — don't edit it manually.
- Unit tests: vitest, only covers
src/shared/tools/**/*.spec.ts - Storybook tests:
@storybook/test-runner+ Playwright, runs against built Storybook - CI storybook test:
pnpm test:storybook:ci(starts server then runs tests)
- Uses Tailwind CSS v4 with custom design tokens (colors, shadows, typography)
- Color palette and shadows defined in
src/shared/tools/tailwind-config.ts - Generated CSS files in
src/shared/styles/generated/are gitignored and must be regenerated before linting/building - Components get styles via
TailwindStyledElement(style)(injects both tailwind base + component CSS) - The
:rootCSS variables are set byTailwindElementbase class to avoid duplication
Uses pnpm (managed via mise). Do not use npm or yarn.