brumate

Markup

The markup of a component is HTML with {…} expressions, attribute values without quotes, event handlers, and # blocks for control flow. Markup that never changes ships as HTML only.

Expressions

{expression} inserts a value. It updates when the values it reads change:

App.brmEdit the code to run it again

The sample runs when it comes into view.

Attributes

An attribute takes a quoted string, a value without quotes, or an expression in parentheses:

<input type="text" value=draft maxlength=(limit - 1) disabled=(!editable)>
<img src=user.avatar alt=(`Photo of ${user.name}`)>

Shorthands:

<a {href}>Open</a>            <!-- the same as href=href -->
<button {onClick}>Go</button>  <!-- the same as onClick=onClick, a handler -->
<input {...rest}>              <!-- every property of rest as an attribute -->
<div [name]=value></div>       <!-- the attribute name is an expression -->

class and style

class takes a string, an array, or an object whose keys are class names that apply while their values are true. style takes a string, or an object of properties in camel case:

App.brmEdit the code to run it again

The sample runs when it comes into view.

{ on } is the class on while on is true. In an array, such as class=(['task', on && 'on']), empty and false items are skipped. A style property whose value is null, undefined or false is left out.

Dynamic tags

<{tag}> renders the element or component that tag names. Close it with </>:

App.brmEdit the code to run it again

The sample runs when it comes into view.

When tag changes in the browser, the element renders again with the new tag.

A tag with no content can close itself, as a void element or a component with no children does:

<script>
  import { $props } from 'brumate/signals';

  const { as = 'hr' } = $props<{ as?: string }>();
</script>

<{as} class="divider" />

</> is the only closing tag of a dynamic tag. </{tag}> is an error.

HTML strings

Inserted text is escaped. raw from brumate/html inserts a string as HTML, such as the HTML of a Markdown file:

<script>
  import { raw } from 'brumate/html';
  import { html } from './post.md';
</script>

<article>{raw(html)}</article>

Insert only HTML that you trust. HTML from a user can run scripts in your page.

Events

onEvent=handler runs the handler when the event fires. The name after on is the DOM event, so onClick listens to click and onKeyDown to keydown. The handler gets the event:

App.brmEdit the code to run it again

The sample runs when it comes into view.

The handler is a name of a function, an arrow function in parentheses, or a call that returns a function. Its code loads with the page. An event that comes before the code has loaded waits for it.

Before the handler loads

A handler runs after the event has finished dispatching, so the browser has acted on the event by then. brumate/events wraps a handler with what has to happen at once:

Wrapper Effect
prevent(handler) Calls event.preventDefault().
prevent(handler, ...keys) On a keyboard event, prevents only these keys. The handler still runs for every key.
stop(handler) Stops the event at this element, so the handlers of the elements around it do not run.
once(handler) Runs the handler for the first event only.
capture(handler) Runs the handler before the handlers of the elements inside, as a listener in the capture phase.

Handlers run as if each element had its own listener. Those wrapped by capture run first, from the outside in, and then the others, from the target outwards. An event that does not bubble, such as focus, blur, mouseenter or scroll, runs only the handlers of its target and the capture handlers around it. Listeners are passive unless a handler of the event prevents, so there is no passive wrapper.

<script>
  import { prevent, stop } from 'brumate/events';
</script>

<ul role="listbox" tabindex="0" onKeyDown=(prevent(move, 'ArrowDown', 'ArrowUp', 'Home', 'End'))>
  …
</ul>
<button onClick=(stop(prevent(save)))>Save</button>

A modifier after the event name, as in onSubmit:prevent=send, does the same, with a warning that points to the wrapper. It goes in a later version. A modifier on a component is a compile error: the component puts the wrapper on the element that it renders, such as a listbox that prevents the keys it handles.

Animating a change

transition(handler) from brumate/events runs the handler inside a view transition. The browser animates from the page before the handler to the page after it, including elements that #if or #for removes and items that change order. CSS styles the animation.

App.brmEdit the code to run it again

The sample runs when it comes into view.

Window and document events

<window> and <document> listen to events of the window and the document. They must close with />:

App.brmEdit the code to run it again

The sample runs when it comes into view.

Their listeners start when the page resumes, and stop when the component is removed.

Accessibility warnings

The compiler warns about common accessibility mistakes. The warnings show in the build, in the editor and in brumate-tsc:

An element with a spread or a computed attribute is not checked, because any attribute can be in it. a11y: false in the plugin options turns the warnings off.

$id

$id() returns an ID that is unique in the page. Use it to connect a label or a description to a field:

App.brmEdit the code to run it again

The sample runs when it comes into view.

App.brmEdit the code to run it again

The sample runs when it comes into view.