# Olum (olumjs) > Olum is a small, Vue-like JavaScript framework where components are plain `.html` files with a > `
Count: {state.count}
``` - The **filename is the component name** — `CounterCard.html` → ``. Component tags are **PascalCase**; that's how the compiler distinguishes a component from a plain element. - ` ``` --- ## Text interpolation `{expr}` in text is evaluated as JS and **HTML-escaped by default** (XSS-safe). ```html

Hello {state.user.name}

Total: {state.items.length} items

{state.count > 0 ? 'positive' : 'zero'}

``` - `null` / `undefined` render as an empty string. - No escape for a literal `{` in text — any `{…}` is interpolation. Use `{String.fromCharCode(123)}` or a const holding `"{"`. --- ## Attributes **String attributes (default):** a literal string; use `{expr}` inside for dynamic parts. ```html
Profile
box
``` **Code attributes (value is an expression):** `when`, `each`, `key`, `on*`, `html` — the `""` value is JS. ```html ``` **Boolean attributes (presence toggle):** when the whole value is a single `{expr}`, the attribute is emitted when truthy, omitted when falsy. ```html
``` Recognized: `checked, disabled, selected, readonly, required, hidden, autofocus, multiple, open, loop, muted, controls, autoplay, novalidate, default, defer, ismap, reversed`. Only applies when value is exactly one `{expr}`. --- ## Events Native `on*` attributes hold **code** (like real HTML). Two forms: ```html ``` - **No event modifiers.** Do it in the handler: `onsubmit="(e)=> { e.preventDefault(); save() }"`. - **Several `on*` attributes per element are fine** — each is wired as its own listener (``). Use `onMount` only for listeners on `window`/`document` or ones needing `capture`/`passive`. - Inside ``, handlers can reference loop variables directly: ` ``` ```js const onInvalid = (e) => { e.preventDefault(); // stop the native bubble, render the message yourself state.errors = { ...state.errors, [e.target.name]: e.target.validationMessage }; }; const handleSubmit = (e) => { e.preventDefault(); if (!e.target.noValidate && !e.target.checkValidity()) return; /* send */ }; // rule no attribute can express: field.setCustomValidity(ok ? "" : "message") — "" means valid ``` **Form events:** `input`/`change` **bubble**, so one listener on the `
` covers every field. ```html ``` - Always `e.preventDefault()` in `onsubmit` (SPA — no page reload). `e.submitter` = the button that sent it (`