Help us improve
Share bugs, ideas, or general feedback.
From duskmoon-ui
Use the DuskMoon Elements custom element library (`<el-dm-*>` web components). Use when building web pages or apps with DuskMoon elements, registering elements, setting properties/attributes, listening to events, using slots, applying themes, or styling with CSS custom properties. Covers all 42 element packages (button, card, input, dialog, table, tabs, markdown-input, code-block, pro-data-grid, and more).
npx claudepluginhub gsmlg-dev/code-agent --plugin duskmoon-uiHow this skill is triggered — by the user, by Claude, or both
Slash command
/duskmoon-ui:duskmoon-elementsThe summary Claude sees in its skill listing — used to decide when to auto-load this skill
42 custom element packages built on `@duskmoon-dev/el-base`. Each element is a standard Web Component with Shadow DOM.
Guides building UI components, implementing themes, and creating responsive layouts with daisyUI and Tailwind CSS. Includes component categories, theming, and installation.
Guides building UI components, implementing themes, and creating responsive designs with daisyUI Tailwind CSS library.
Guides DaisyUI v5 best practices, shadcn/ui to DaisyUI migration mappings, oklch theme configuration, color transitions, and component catalog for class-based Tailwind UIs.
Share bugs, ideas, or general feedback.
42 custom element packages built on @duskmoon-dev/el-base. Each element is a standard Web Component with Shadow DOM.
# Individual element
bun add @duskmoon-dev/el-button
# All elements at once
bun add @duskmoon-dev/elements
// Option 1: Explicit (tree-shakable)
import { register } from '@duskmoon-dev/el-button';
register();
// Option 2: Side-effect auto-register
import '@duskmoon-dev/el-button/register';
// Option 3: Register all elements
import { registerAll } from '@duskmoon-dev/elements';
registerAll();
<el-dm-button variant="filled" color="primary" size="md">
Click me
</el-dm-button>
<el-dm-dialog id="my-dialog">
<span slot="header">Title</span>
Dialog content here.
<div slot="footer">
<el-dm-button onclick="this.closest('el-dm-dialog').hide()">Close</el-dm-button>
</div>
</el-dm-dialog>
Set via HTML attributes (kebab-case) or JS properties (camelCase). Properties with reflect: true sync both directions.
<!-- HTML attributes -->
<el-dm-button variant="outlined" disabled>Save</el-dm-button>
<!-- JS properties -->
<script>
const btn = document.querySelector('el-dm-button');
btn.variant = 'outlined';
btn.disabled = true;
</script>
Common properties across elements:
| Property | Type | Description |
|---|---|---|
variant | String | Visual variant (filled, outlined, soft, text, ghost) |
color | String | Color theme (primary, secondary, success, warning, error, info) |
size | String | Size (sm, md, lg) |
disabled | Boolean | Disable interaction |
Complex data (arrays, objects) must be set via JS — use attribute: false:
const table = document.querySelector('el-dm-table');
table.columns = [{ field: 'name', header: 'Name' }];
table.data = [{ name: 'Alice' }, { name: 'Bob' }];
Listen with addEventListener. Events bubble and are composed (cross shadow DOM).
const table = document.querySelector('el-dm-table');
table.addEventListener('sort', (e) => {
console.log(e.detail); // { column: 'name', direction: 'asc' }
});
table.addEventListener('row-click', (e) => {
console.log(e.detail); // { row: {...}, rowIndex: 0 }
});
Common events:
| Element | Event | Detail |
|---|---|---|
| dialog | open, close | — |
| table | sort | { column, direction } |
| table | select | { selectedIds, selectedRows } |
| table | page-change | { page, pageSize } |
| pagination | page-change | { page } |
| tabs | tab-change | { index, tab } |
| input | dm-input, dm-change | { value } |
Named slots project light DOM content into the element's shadow DOM.
<el-dm-button>
<span slot="prefix">🔍</span>
Search
<span slot="suffix">→</span>
</el-dm-button>
<el-dm-card>
<span slot="header">Card Title</span>
Card body content
<div slot="footer">Footer</div>
</el-dm-card>
Common slots: (default), header, footer, prefix, suffix, empty, actions.
Style shadow DOM internals from outside using ::part():
el-dm-button::part(button) {
border-radius: 0;
}
el-dm-dialog::part(backdrop) {
backdrop-filter: blur(4px);
}
el-dm-table::part(thead) {
background: var(--color-surface-container-high);
}
Elements use CSS custom properties from @duskmoon-dev/el-base. Apply a preset theme:
import { applyTheme } from '@duskmoon-dev/el-base';
applyTheme(document.documentElement, 'moonlight'); // dark
applyTheme(document.documentElement, 'sunshine'); // light
// Also: 'ocean', 'forest', 'rose'
Override individual variables:
:root {
--color-primary: oklch(60% 0.15 250);
--color-surface: #ffffff;
}
Key variables: --color-primary, --color-surface, --color-on-surface, --color-outline, --color-surface-container.
See references/core-api.md for full CSS variable list.
Property changes are batched via queueMicrotask. Multiple changes in the same tick produce a single re-render:
const el = document.querySelector('el-dm-button');
el.variant = 'outlined';
el.color = 'error';
el.size = 'lg';
// → single re-render
For CSS art elements (<el-dm-art-*>), see the duskmoon-art-elements skill.