Skip to content

Your First Component

The easiest way to learn Ornata is to enhance a small piece of existing HTML with a clear component contract.

<section data-disclosure>
<button data-disclosure-button>Show details</button>
<div hidden data-disclosure-panel>
<p>Details ...</p>
</div>
</section>
import { defineComponent } from 'ornata';
const Disclosure = defineComponent({
name: 'Disclosure',
state: {
open: { default: false, type: Boolean },
openLabel: { default: 'Hide details' },
closedLabel: { default: 'Show details' },
},
elements: {
button: { query: '[data-disclosure-button]' },
panel: { query: '[data-disclosure-panel]' },
},
methods: {
toggle() {
this.state.open = !this.state.open;
},
},
watch: {
open({ newValue, oldValue, isInitial }) {
if (isInitial) return;
console.log(
`Disclosure's open state changed from ${oldValue} to ${newValue}`
);
},
},
computed: {
buttonLabel() {
if (this.state.open) return this.state.openLabel;
return this.state.closedLabel;
},
},
render: {
button() {
return {
text: this.computed.buttonLabel,
attributes: {
type: 'button',
'aria-expanded': String(this.state.open),
},
events: {
click: () => this.methods.toggle(),
},
};
},
panel() {
return {
attributes: {
hidden: !this.state.open,
},
};
},
},
});
const disclosure = Disclosure.mount('[data-disclosure]');

Each top-level option has a clear job:

  • state defines reactive values
  • elements finds important DOM nodes inside the root
  • methods defines reusable internal behavior
  • watch reacts to state changes
  • computed derives values from state
  • render applies DOM updates to resolved elements

That separation is one of Ornata’s main strengths. It keeps the contract between markup, behavior, and state visible, which makes the component easier to reuse in larger HTML-first systems.

Continue to Component Anatomy for a deeper look at every option section.

If you want a fuller walkthrough of Ornata’s explicit typing patterns and TypeScript helpers, read TypeScript.