You can create elements using Dom.TagName like Dom.div, Dom.h1, and so on.
Dom.div;
Dom.section;
Dom.p;
Dom.h1;
Dom.span;
Dom.img;
...
Nest elements using .set({ children }) or .children([]) to build complex structures easily.
Dom.ul.set({
children: [
Dom.li,
Dom.li,
Dom.li,
Dom.li,
...
]
})
//or
Dom.ul.children([
Dom.li,
Dom.li,
Dom.li,
Dom.li,
...
])
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
...
</ul>
<ul>
<li></li>
<li></li>
<li></li>
<li></li>
...
</ul>
Dom.ul.set({
children: [
Dom.li.set({
children: [
Dom.a.set({
children: [
Dom.i,
Dom.span
]})
]
}),
Dom.li.set({
children: [
Dom.a.set({
children: [
Dom.i,
Dom.span
]})
]
}),
Dom.li.set({
children: [
Dom.a.set({
children: [
Dom.i,
Dom.span
]})
]
}),
]
})
<ul>
<li>
<a>
<i></i>
<span></span>
</a>
</li>
<li>
<a>
<i></i>
<span></span>
</a>
</li>
<li>
<a>
<i></i>
<span></span>
</a>
</li>
</ul>
You can configure a component using the .set({ ... }) method.
This allows you to set attributes, styles, events, dataset, and more β all in a single declarative call.
Dom.div.set({
id: 'myDiv',
class: 'box type-1',
style: {
width: '100px',
height: '100px',
display: 'flex',
justifyContent: 'center',
alignItems: 'center',
color: '#fff',
border: '1px solid #000',
background: 'black'
},
text: "Dom Craft",
attr: {
someAttr: 'attr value'
},
dataset: {
role: 'box'
},
on: {
click: () => {
alert('click!');
}
}
...
})
<div
id="myDiv"
class="box type-1"
someattr="attr value"
data-role="box"
style="
width: 100px;
height: 100px;
border: 1px solid rgb(0, 0, 0);
background: black;
"
>Dom Craft</div>
DomCraft provides a simple and lightweight reactivity system with state() and effect().
You can create reactive values using Dom.state(initialValue) and subscribe to changes using Dom.effect(fn, [deps]).
When the state updates, all dependent effects are automatically re-evaluated.
This enables you to build dynamic, interactive UI without needing any complex framework or virtual DOM.
const [count, setCount] = Dom.state(0);
const button = Dom.button;
Dom.effect(() => {
button.text(`Clicked: ${count()} times`);
}, [count]);
button.set({
on: {
click: () => setCount(count() + 1)
}
});
DomCraft allows you to bind native DOM events declaratively using the on property.
Simply pass an object of event listeners (like click, input, submit, etc) inside the .set() method.
Handlers are automatically bound to the element and scoped cleanly β no manual addEventListener required.
const P = Dom.p.set({text: 'You typed: '});
const Input = Dom.input.set({
type: 'text',
placeholder: 'Type something...',
on: {
input: (e) => {
console.log(`
You typed: ${e.target.value}
`);
}
}
});
<p>You typed: </p>
<input
type="text"
placeholder="Type something..."
>
You can apply styles, attributes, and dataset values declaratively using the .set() method.
Use:
style β to apply inspanne CSS styles as a JS object
attr β to add native HTML attributes (e.g. type, placeholder)
dataset β to define data-* attributes
These configurations make element setup clean and centralized.
const box = Dom.div.set({
id: 'myBox',
class: 'highlighted',
style: {
width: '200px',
height: '100px',
backgroundColor: '#4CAF50',
color: 'white',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
borderRadius: '8px'
},
text: 'Styled Box',
attr: {
title: 'Hover tooltip',
role: 'presentation'
},
dataset: {
type: 'feature',
index: '1'
}
});
<div
id="myBox"
class="highlighted"
title="Hover tooltip"
role="presentation"
data-type="feature"
data-index="1"
style="
width: 200px;
height: 100px;
background-color: #4CAF50;
color: white;
display: flex;
align-items: center;
justify-content: center;
border-radius: 8px;
"
>
Styled Box
</div>
DomCraft supports animations using the native Element.animate() API.
You can apply keyframes and options via the .animations() method.
Built-in presets such as fadeIn, slideUp are available through Dom.presets.ani.
const AnimatedBox = Dom.div.set({
class: 'box',
text: 'I will slide in!',
style: {
width: '150px',
height: '80px',
background: '#2196F3',
color: '#fff',
display: 'flex',
alignItems: 'center',
justifyContent: 'center',
marginTop: '20px'
}
});
const Button = Dom.button.set({
text: 'animate start',
on: {
click: () => {
AnimatedBox.animations({
keyframe: Dom.presets.ani.slideUp,
options: {
duration: 500,
easing: 'ease-out',
fill: 'both'
}
})
}
}
})
DomCraft provides an easy wrapper around IntersectionObserver using Dom.inView().
This allows you to detect when an element enters or leaves the viewport, perfect for scroll-triggered effects or lazy loading.
You can attach in and out handlers directly on the element.
const target = Dom.div.set({
class: 'inview-box',
text: 'Watch me fade in/out!',
style: {
opacity: 0,
transition: 'opacity 0.5s ease',
marginTop: '100vh',
padding: '50px',
background: '#333',
color: '#fff',
textAlign: 'center'
}
});
target.in = () => {
target.style({ opacity: 1 });
};
target.out = () => {
target.style({ opacity: 0 });
};
const observer = Dom.inView(0.3);
observer.watch(target);
Use Dom.injectCss() to dynamically insert custom CSS into your document.
This is useful for styling components on the fly, injecting theme variables, or adding demo styles without external files.
Dom.injectCss(`
.box {
width: 120px;
height: 120px;
background: dodgerblue;
color: white;
font-weight: bold;
display: flex;
justify-content: center;
align-items: center;
border-radius: 8px;
transition: transform 0.3s ease;
}
.box:hover {
transform: scale(1.05);
}
`);
Dom.div.set({
class: 'box',
text: 'Hover me!'
})
You can define your own components as plain functions.
These functions return DomCraft elements and can include internal logic, state, and even effects.
This makes it easy to encapsulate UI logic and reuse components across your app.
const Todo = () => {
const wrapper = Dom.div;
const input = Dom.input;
const list = Dom.ul;
const form = Dom.form.set({
on: {
submit: (e) => {
e.preventDefault();
if (input.el.value.trim()) {
list.append(Dom.li.set({ text: input.el.value }));
input.el.value = '';
}
}
},
children: [
input.set({
type: 'text',
attr: { placeholder: 'Add todo...' }
}),
Dom.button.set({
type: 'submit',
text: 'Add'
})
]
});
wrapper.children([form, list]);
return wrapper;
};
This example demonstrates how to create a skeleton loading interface using Dom.state() and Dom.effect().
Each item initially renders a placeholder with a pulsing animation, simulating a loading state.
After a short delay, the actual content is loaded asynchronously and the skeleton is replaced with real data.
This pattern is ideal for improving perceived performance in situations where data fetching takes time.
This is an experimental component with ongoing improvements and regular updates.
Dom.injectCss(`
.ui-item {
width: 220px;
background: #fff;
border-radius: 5px;
padding: 20px;
}
.ui-item + .ui-item {
margin-top: 10px;
}
.ui-title {
font-size: 16px;
color: #222;
margin-bottom: 10px;
height: 1em;
}
.ui-desc {
font-size: 13px;
color: #666;
height: 1em;
}
@keyframes pulse {
0% { opacity: 1 }
50% { opacity: 0.6 }
100% { opacity: 1 }
}
.pulse {
animation: pulse 0.5s infinite;
width: 100%;
background: #ddd;
}
`)
const [datas, setDatas] = Dom.state([]);
Dom.effect(() => {
datas().forEach(([data, setData]) => {
setData(data());
})
}, [datas])
const ListItem = (key) => {
const Item = Dom.div.class('ui-item');
const Title = Dom.p.class('ui-title pulse');
const Desc = Dom.p.class('ui-desc pulse');
const [data, setData] = Dom.state(null);
setDatas([...datas(), [data, setData]])
Dom.effect(() => {
if (data()) {
Title.set({
html: data().title
});
Desc.set({
html: data().desc
});
Title.removeClass('pulse');
Desc.removeClass('pulse');
} else {
Title.set({
html: ''
});
Desc.set({
html: ''
});
Title.class('pulse');
Desc.class('pulse');
}
}, [data])
Item.set({
children: [
Title,
Desc
]
})
const getData = async (key) => {
await Dom.delay(600 + Math.random() * 300);
setData({
title: `item0${key}`,
desc: `hello, this is item0${key}`
})
}
getData(key);
return Item;
}
const ReloadButton = Dom.button.set({
text: 'Reload',
style: {
position: 'absolute',
top: '20px',
right: '20px',
padding: '5px'
},
on: {
click: () => {
Dom.qs('#sample09').replace([
ListItem(1),
ListItem(2),
ListItem(3),
ListItem(4),
ListItem(5),
ReloadButton,
])
}
}
})
Dom.qs('#sample09').append([
ListItem(1),
ListItem(2),
ListItem(3),
ListItem(4),
ListItem(5),
ReloadButton,
])