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-uiThis skill uses the workspace's default tool permissions.
42 custom element packages built on `@duskmoon-dev/el-base`. Each element is a standard Web Component with Shadow DOM.
Guides Next.js Cache Components and Partial Prerendering (PPR) with cacheComponents enabled. Implements 'use cache', cacheLife(), cacheTag(), revalidateTag(), static/dynamic optimization, and cache debugging.
Migrates code, prompts, and API calls from Claude Sonnet 4.0/4.5 or Opus 4.1 to Opus 4.5, updating model strings on Anthropic, AWS, GCP, Azure platforms.
Automates semantic versioning and release workflow for Claude Code plugins: bumps versions in package.json, marketplace.json, plugin.json; verifies builds; creates git tags, GitHub releases, changelogs.
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.