Quick Start

Create components

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;
    ...
        
      

Nested components

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>
          
        

Set components

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>
          
        

Reactive State

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)
      }
    });
        
        

Event Handling

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..." 
    >
          
        

Styling & Attributes

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>
          
        

Animation Support

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'
            }
          })
        }
      }
    })
        
        

In-View Observer

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);
        
        
Scroll Down πŸ‘‡

Inject CSS

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!'
    })
        
        

Custom Components

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;
    };
        
        

Example Code

Skeleton UI

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,
    ])