@grayscale-dev/dragon
v0.1.21
Published
Framework-agnostic Web Components built with Lit.
Readme
Dragon UI
Framework-agnostic Web Components built with Lit.
Components
<dui-input>: plain text input.<dui-button>: native-like button with submit/reset behavior.<dui-button-group>: merges multiple dui-button elements into one segmented control.<dui-mask-input>: Inputmask-powered masked input + presets/templates.<dui-number-input>: numeric input (decimal defaults).<dui-currency-input>: currency-focused number input.<dui-date-input>: date-focused masked input (YYYY-MM-DDdefaults).<dui-dropdown>: hybrid dropdown/select/combobox with searchable + multi-select modes.
Build
npm install
npm --workspace packages/ui run buildManifest Build
npm --workspace packages/ui run build:manifestbuild always generates dist/manifest.json and validates it with zod.
Unit Tests
npm --workspace packages/ui run testUsage (Vanilla)
<script type="module">
import '@grayscale-dev/dragon';
</script>
<dui-input label="Name"></dui-input>
<dui-button type="button" size="md" severity="plain" variant="standard" label="Save" badge="3"></dui-button>
<dui-mask-input template-name="phone-us"></dui-mask-input>
<dui-number-input min="0" step="0.5"></dui-number-input>
<dui-currency-input currency="USD" locale="en-US"></dui-currency-input>
<dui-date-input></dui-date-input>
<dui-dropdown label="Status" label-position="floating" searchable max-items-shown="6"></dui-dropdown>Usage (React)
import '@grayscale-dev/dragon';
export function Demo() {
return (
<>
<dui-button type="submit" size="lg" severity="success" variant="outline" label="Save" badge="2" badge-severity="danger"></dui-button>
<dui-mask-input
template-name="phone-us"
onInput={(e) => {
const el = e.currentTarget as HTMLInputElement & { value: string };
console.log(el.value);
}}
/>
<dui-dropdown
searchable
onInput={(e) => {
const el = e.currentTarget as HTMLElement & { value: string };
console.log(el.value);
}}
/>
</>
);
}Manifest Consumption
Machine-readable docs/theme-builder metadata is exported at @grayscale-dev/dragon/manifest.
import manifest from '@grayscale-dev/dragon/manifest';
for (const component of manifest.components) {
console.log(component.tag, component.cssTokens.length);
}Generate docs/theme controls from this manifest only.
Manifest Examples Metadata
Each component can include examples so docs/playgrounds render usage samples without hardcoded data.
{
"tag": "dui-input",
"examples": {
"groups": [
{ "id": "basic", "label": "Basic", "order": 1 }
],
"items": [
{
"id": "basic-plain",
"group": "basic",
"title": "Plain Input",
"order": 1,
"preview": {
"attributes": { "placeholder": "Select status", "searchable": true },
"options": [
{ "value": "new", "label": "New" },
{ "value": "done", "label": "Done" }
],
"value": "new",
"tokens": { "--ui-input-border": "1px solid #94a3b8" }
},
"snippets": [
{ "framework": "vanilla", "lang": "html", "code": "<dui-input></dui-input>" }
]
}
]
}
}Consumer behavior:
- Sort
examples.groupsbyorder. - For each group, show
examples.itemswhereitem.groupmatches, sorted byorder. - Render snippet framework tabs/selectors from
item.snippets[].framework. - Use
item.preview(attributes,tokens,options,value,values,notes) as live preview defaults.
Presets
<dui-mask-input template-name="..."> supports:
phone-usphone-intlssneinzip-uszip-plus4-uscredit-cardcvvexpirydate-isotime-24hcurrency-usd
You can also import presets:
import { DUI_INPUT_MASK_PRESETS } from '@grayscale-dev/dragon';Inputmask Config Example
<dui-mask-input
input-mask="decimal"
input-mask-config='{"alias":"numeric","groupSeparator":",","autoGroup":true,"digits":2,"radixPoint":".","prefix":"$ ","removeMaskOnSubmit":true}'
></dui-mask-input>Styling
Button sizes: xs, sm, md (default), lg, xl.
Button severities: plain (default), primary, secondary, success, info, warning, help, danger.
Button variants: standard (default), ghost, outline, round.
Button badges: set badge and optional badge-severity (plain default).
Shared CSS custom properties:
--ui-input-padding--ui-input-font-size--ui-input-border--ui-input-radius--ui-input-bg--ui-input-color--ui-input-placeholder-color--ui-input-focus-ring--ui-input-label-font-size--ui-input-label-color--ui-input-floating-label-left--ui-input-floating-padding-top--ui-input-floating-padding-right--ui-input-floating-padding-bottom--ui-input-floating-padding-left--ui-button-padding--ui-button-font-size--ui-button-font-weight--ui-button-border--ui-button-radius--ui-button-min-width--ui-button-bg--ui-button-hover-bg--ui-button-active-bg--ui-button-color--ui-button-focus-ring--ui-button-disabled-opacity--ui-button-<severity>-bg--ui-button-<severity>-hover-bg--ui-button-<severity>-active-bg--ui-button-<severity>-color--ui-button-<severity>-border--ui-button-badge-size--ui-button-badge-padding-inline--ui-button-badge-font-size--ui-button-badge-font-weight--ui-button-badge-bg--ui-button-badge-color--ui-button-badge-border--ui-button-badge-<severity>-bg--ui-button-badge-<severity>-color--ui-button-badge-<severity>-border--ui-button-group-radius--ui-button-group-border-overlap--ui-dropdown-chevron-color--ui-dropdown-panel-border--ui-dropdown-panel-radius--ui-dropdown-panel-bg--ui-dropdown-panel-shadow--ui-dropdown-option-height--ui-dropdown-option-padding--ui-dropdown-option-hover-bg--ui-dropdown-option-selected-bg--ui-dropdown-chip-bg--ui-dropdown-chip-color--ui-dropdown-chip-radius
Example:
dui-input,
dui-mask-input,
dui-number-input,
dui-currency-input,
dui-date-input {
--ui-input-padding: 12px 16px;
--ui-input-border: 1px solid #94a3b8;
--ui-input-radius: 10px;
}
/* components expose parts for internal controls */
dui-mask-input::part(input) {
font-weight: 600;
}
dui-button::part(button) {
letter-spacing: 0.01em;
}