Klenodexample

Docs

Haml Components

How Haml modules become components, how companion files are imported, and how scoped selectors attach to rendered tags.

Haml Modules

HamlPlugin transforms .haml files into Ruby component classes exported as Default.

:ruby
  Button = import("/components/Button")

%article
  %h1= $title
  %Button Open

Haml component references such as %Button compile to factory calls using the imported component class as the tag.

When HamlPlugin is configured with global_variables: "@__props", app-style global variable reads become prop lookups. In the example above, $title compiles to @__props[:title]. This keeps templates concise while leaving the props storage object under framework control.

Ruby And Filters

= prints a value. - runs Ruby silently. Ruby filters define class-level code for imports and methods, while later Ruby script blocks inside the template render method can access request-local helpers.

Haml also supports inline :css and :markdown filters. Markdown filters are handled by MarkdownPlugin and use /markdown-components.rb when that map exists.

Children And Slots

Component children are exposed through $children. Named slots are still children; they are selected by slot name from the same object.

%PopoverButton{ title: "Choose language" }
  %span.icon(slot="button")
  %span.code(slot="button") EN

  %a(href="/") English
  %a(href="/sv") Svenska

A receiving component can render the default slot with $children and a named slot with $children[:button].

%button
  = $children[:button]

%div(popover)
  = $children

Components can also use %slot when they want declarative insertion points:

%button
  %slot(name="button")

%div(popover)
  %slot

The example framework wraps children in a small object, so components can ask $children.empty?, $children.any?, $children.to_a, or $children.text_content without reaching into framework internals.

Companion Files

Haml automatically imports matching CSS companions as ClassNames when present. The companion naming follows the route-file convention, so +page.haml pairs with +page.css.

components/Card.haml
components/Card.css

routes/docs/+page.haml
routes/docs/+page.css

It also watches Component.intl.*.toml translation companions. Adding, editing, or removing companion files invalidates the owning Haml module in development.

Translations

Translation companions are powered by IntlPlugin. HamlPlugin can run without it, but then intl companion files are ignored and the generated component receives an empty translation map.

components/Card.haml
components/Card.intl.en.toml
components/Card.intl.sv.toml

When IntlPlugin is in the build context, Haml components receive locale-keyed translation data through the generated component class. Applications decide how to expose that data to templates. A framework might provide an I18n.t(:summary) helper, while keeping locale negotiation outside Klenod core.

Scoped CSS Maps

CSS is transformed through mayu-css. Ruby or Haml importing CSS receives a class-name map.

article {
  display: grid;
}

.featured {
  border-color: var(--color-accent);
}

Tag selectors use __-prefixed keys such as ClassNames[:__article]. Class selectors use normal keys such as ClassNames[:featured].

Haml applies scoped tag classes automatically to matching tags. Explicit Haml classes such as %article.featured are joined with the matching scoped class from the same CSS module.

Scope Boundaries

Scoped CSS belongs to the component or page that renders the element. A selector in routes/docs/+layout.css can style the sidebar links rendered by routes/docs/+layout.haml, but it cannot reliably style paragraph or list tags rendered inside $children by another page or Markdown component.

Prefer smaller components for reusable styled elements. If a layout renders child content from another module, style that child content through components owned by the child module rather than through broad parent selectors.

Source Maps And Errors

Generated Ruby includes source map marks so runtime exceptions can be rewritten back to original Haml source lines. Parse errors show source context directly, without requiring readers to inspect generated Ruby.