adunaic
v1.3.3
Published
Shadow DOM UI components designed to complement Elvish layout primitives
Downloads
199
Maintainers
Readme
Adûnaic
Shadow DOM UI Components designed to complement Elvish layout primitives.
Adûnaic provides encapsulated, accessible web components for common UI patterns—dialogs, buttons, sliders, tabs, and more—while Elvish handles layout. Together, they form a complete, no-build-required toolkit for modern web development.
Philosophy
| Library | Purpose | DOM Strategy |
|---------|---------|--------------|
| Elvish | Layout primitives (<i-hath>, <i-enedh>, etc.) | Light DOM, embraces cascade |
| Adûnaic | UI components (<an-kuph>, <an-tabar>, etc.) | Shadow DOM, encapsulates |
The key insight: Elvish layouts work inside Adûnaic components because user content is slotted into the Light DOM.
<an-kuph id="my-dialog">
<!-- Elvish layout primitives work perfectly here! -->
<i-hath space="var(--space-m)">
<h2 class="text-step-3">Dialog Title</h2>
<p>Your content with global CSS utilities.</p>
<an-tabar variant="primary" onclick="this.closest('an-kuph').close()">
Close
</an-tabar>
</i-hath>
</an-kuph>Installation
CDN / Script Tag (No Build Required)
<!-- Minified bundle -->
<script src="https://unpkg.com/adunaic/dist/adunaic.min.js"></script>
<!-- With Elvish -->
<link rel="stylesheet" href="https://unpkg.com/elvish-css/dist/elvish.min.css">NPM
npm install adunaic// Import all components
import 'adunaic';
// Or import specific components
import { AnKuph, AnTabar, toast } from 'adunaic';Vendoring (Copy to Your Project)
Download dist/adunaic.min.js and include it directly:
<script src="/vendor/adunaic.min.js"></script>Components
Core Components
| Component | Tag | Description |
|-----------|-----|-------------|
| Dialog | <an-kuph> | Modal dialog with focus trap, backdrop, animations |
| Button | <an-tabar> | Enhanced button with variants, loading state |
| Switch | <an-zal> | Toggle switch |
| Tabs | <an-liphim> | Tab interface with keyboard navigation |
| Toast | <an-rash> | Toast notifications |
| Alert | <an-hazar> | Inline alert banners |
| Progress | <an-ganad> | Progress bar (determinate/indeterminate) |
| Spinner | <an-dawar> | Loading spinner |
| Slider | <an-saril> | Single-value range slider |
| Range Slider | <an-saril-tad> | Dual-thumb range slider |
Utility Components (Light DOM)
| Component | Tag | Description |
|-----------|-----|-------------|
| Focus Trap | <an-nazar-dak> | Traps keyboard focus within children |
Usage Examples
Dialog (an-kuph)
<an-kuph id="confirm-dialog" closable close-on-backdrop>
<i-hath space="var(--space-m)">
<h2>Confirm Action</h2>
<p>Are you sure you want to proceed?</p>
<i-lath justify="end" space="var(--space-xs)">
<an-tabar variant="ghost" onclick="document.getElementById('confirm-dialog').close()">
Cancel
</an-tabar>
<an-tabar variant="primary" onclick="handleConfirm()">
Confirm
</an-tabar>
</i-lath>
</i-hath>
</an-kuph>
<an-tabar onclick="document.getElementById('confirm-dialog').showModal()">
Open Dialog
</an-tabar>Button (an-tabar)
<!-- Variants -->
<an-tabar variant="primary">Primary</an-tabar>
<an-tabar variant="secondary">Secondary</an-tabar>
<an-tabar variant="ghost">Ghost</an-tabar>
<an-tabar variant="danger">Danger</an-tabar>
<an-tabar variant="outline">Outline</an-tabar>
<!-- Sizes -->
<an-tabar size="sm">Small</an-tabar>
<an-tabar size="md">Medium</an-tabar>
<an-tabar size="lg">Large</an-tabar>
<!-- States -->
<an-tabar loading>Loading...</an-tabar>
<an-tabar disabled>Disabled</an-tabar>
<!-- Full width -->
<an-tabar block>Full Width</an-tabar>Switch (an-zal)
<an-zal>Enable notifications</an-zal>
<an-zal checked>Dark mode</an-zal>
<an-zal disabled>Premium feature</an-zal>
<script>
document.querySelector('an-zal').addEventListener('change', (e) => {
console.log('Checked:', e.detail.checked);
});
</script>Tabs (an-liphim)
<an-liphim value="tab1" indicator>
<div data-tab="tab1" data-label="Overview">
<p>Overview content here.</p>
</div>
<div data-tab="tab2" data-label="Features">
<p>Features content here.</p>
</div>
<div data-tab="tab3" data-label="Pricing">
<p>Pricing content here.</p>
</div>
</an-liphim>Toast Notifications (an-rash)
import { toast } from 'adunaic';
// Simple usage
toast.success('Changes saved!');
toast.error('Something went wrong');
toast.warning('Session expiring soon');
toast.info('New update available');
// With options
toast.success('File uploaded', {
duration: 8000,
position: 'top-center',
action: {
label: 'View',
onClick: () => window.open('/files')
}
});Slider (an-saril)
<an-saril min="0" max="100" value="50" show-value>
Volume
</an-saril>
<script>
document.querySelector('an-saril').addEventListener('change', (e) => {
console.log('Value:', e.detail.value);
});
</script>Range Slider (an-saril-tad)
<an-saril-tad
min="0"
max="1000"
low="200"
high="800"
step="50"
show-value
name="price"
>
Price Range ($)
</an-saril-tad>
<script>
document.querySelector('an-saril-tad').addEventListener('change', (e) => {
console.log('Range:', e.detail.low, '-', e.detail.high);
});
</script>Styling with ::part()
All Adûnaic components expose CSS parts for styling:
/* Dialog styling */
an-kuph::part(backdrop) {
background: oklch(0% 0 0 / 0.8);
}
an-kuph::part(panel) {
background: var(--color-surface);
border-radius: 16px;
}
/* Button styling */
an-tabar::part(button) {
font-family: 'Inter', sans-serif;
text-transform: uppercase;
}
/* Slider styling */
an-saril-tad::part(range) {
background: linear-gradient(90deg, #4f46e5, #06b6d4);
}
an-saril-tad::part(thumb-low),
an-saril-tad::part(thumb-high) {
border-color: #06b6d4;
}Datastar Integration
Adûnaic components work seamlessly with Datastar:
<an-kuph
id="delete-dialog"
data-on-close="$$delete('/api/item/' + $itemId)"
>
<p>Delete item <span data-text="$itemName"></span>?</p>
<an-tabar data-on-click="$delete-dialog.close('confirm')">
Delete
</an-tabar>
</an-kuph>
<an-zal
data-model="settings.darkMode"
data-on-change="$$patch('/api/settings', {darkMode: $settings.darkMode})"
>
Dark Mode
</an-zal>Custom Properties
Components inherit Elvish's CSS custom properties:
:root {
/* Adunaic will use these from Elvish */
--step-0: 1rem;
--step-1: 1.125rem;
--space-s: 1rem;
--space-m: 1.5rem;
--color-accent: oklch(75% 0.18 168);
}Browser Support
Adûnaic targets modern browsers:
- Chrome 100+
- Edge 100+
- Safari 15+
- Firefox 100+
- Brave (latest)
Naming Convention
Component names use Neo-Adûnaic (the language of Númenor in Tolkien's legendarium):
| English | Adûnaic | Tag |
|---------|---------|-----|
| dialog/box | kuph | an-kuph |
| button/anvil | tabar | an-tabar |
| switch | zal | an-zal |
| tabs | liphim | an-liphim |
| toast/quick | rash | an-rash |
| alert/warning | hazar | an-hazar |
| progress | ganad | an-ganad |
| spinner | dawar | an-dawar |
| slider | saril | an-saril |
| double slider | saril-tad | an-saril-tad |
| focus trap | nazar-dak | an-nazar-dak |
License
MIT License - See LICENSE for details.
Related Projects
- Elvish CSS - Layout primitives
- Datastar - Hypermedia framework
