Ecosyste.ms: Awesome

An open API service indexing awesome lists of open source software.

Awesome Lists | Featured Topics | Projects

https://github.com/loomchild/theo-rails

Theo is a small and elegant HTML-like template language for Ruby on Rails, featuring natural partials and computed attributes.
https://github.com/loomchild/theo-rails

alpinejs erb-template html ruby-gem ruby-on-rails tailwindcss vuejs

Last synced: about 12 hours ago
JSON representation

Theo is a small and elegant HTML-like template language for Ruby on Rails, featuring natural partials and computed attributes.

Awesome Lists containing this project

README

        

[![Gem Version](https://badge.fury.io/rb/theo-rails.svg)](https://badge.fury.io/rb/theo-rails)

# Theo
Theo is a small and elegant HTML-like template language for Ruby on Rails, featuring natural partials and computed attributes.

> [!WARNING]
> Please note that this software is still experimental - use at your own risk.

## Introduction

Thanks to Hotwire, it's now possible to build sophisticated server-rendered user interfaces in Ruby on Rails. However, ERB, Rails' most popular template language, has unintuitive partial syntax, especially for those used to working with Vue.js or React components.

With Theo, you can render a partial using HTML-like syntax:
```html
<_button size="large" label%="label" />
```

## Installation

Run

gem install theo-rails

If you are using TailwindCSS, add `.theo` extension to the `content` key in your `tailwind.config.js`:

'./app/views/**/*.{erb,haml,html,slim,theo}'

## Syntax

### Computed attributes

Computing attribute value in ERB feels awkward because angle brackets `<>` clash with the surrounding HTML tag.

In Theo, an attribute with computed value can be expressed using `%=`. For example:
```html
Home
```
is equivalent to:
```erb
Home
```
> [!TIP]
> Computed attributes work with partials as well as standard HTML tags.

### Partials

Rendering a partial in ERB requires switching between HTML markup and Ruby code, and the `render` verb makes it difficult to imagine a page as a component structure.

In Theo, you render a partial by writing a tag with `_` prefix, for example:
```html
<_button size="large" />`
```
is equivalent to:
```erb
<%= render 'button', size: 'large' %>
```

Naturally, partials can also include content, e.g.:
```html
<_button size="large">
Create

```

> [!TIP]
> Rendered partials can be implemented in ERB, Theo or any other template language.

#### Collections

You can render a collection of partials as follows:
```html
<_widget collection%="widgets" />
```
which is equivalent to:
```erb
<%= render partial: 'widget', collection: widgets %>
```

You can also customize the local variable name via the `as` attribute, e.g.:
```html
<_widget collection%="widgets" as="item" />
```

#### Boolean attributes

If an attribute has no value, you can omit it, for example:
```html
<_button disabled />
```
is equivalent to:
```html
<_button disabled="" />
```

#### Path

To render a partial from another folder, use the 'path' attribute, e.g.:
```html
<_widget path="widgets" />
```
is equivalent to:
```erb
<%= render 'widgets/widget' %>
```

#### `yields` attribute

Partials can yield a value, such as a builder object that can be used by child partials. For example:
```html
<_widget_builder yields="widget">
<_widget_element widget%="widget" />

```
is equivalent to:
```erb
<%= render 'widget_builder' do |widget| %>
<%= render 'widget_element', widget: %>
<% end %>
```

#### `provide` and `inject` helpers

Instead of using `yields` attribute, a parent partial can indirectly pass a variable to its children using the `provide` and `inject` helpers. The example above can be modified as follows:
```html
<_widget_builder>
<_widget_element />

```

`_widget_builder.html.theo`:
```erb
<% provide(widget:) do %>
<%= yield %>
<% end %>
```

`_widget_element.html.theo`:
```erb
<% widget = inject(:widget) %>
```

> [!NOTE]
> This technique is used by [form partials](#form-partials). Use it sparingly, as implicit variables can reduce code readability.

### ERB backwards compatibility

You can freely mix ERB and Theo syntax, e.g.:
```erb
<% if total_amount > 100 %>
<_free_shipping amount%="total_amount" />
<% end %>
```

## Forms

You can build a `` element in ERB using [ActionView form helpers](https://guides.rubyonrails.org/form_helpers.html). Theo provides corresponding partials. For example:
```html
<_form_with model%="widget" data-turbo-confirm="Are you sure?">


<_label name="name" />
<_text_field name="name" />


<_label name="size" />
<_select name="size" options%="['Big', 'Small']" />

<_submit value="Create" />

```
is equivalent to:
```erb
<%= form_with model: widget, data: { turbo_confirm: 'Are you sure?' } do |form| %>


<%= form.label :name %>
<%= form.text_field :name %>


<%= form.label :size %>
<%= form.select :size, ['Big', 'Small'] %>

<%= form.submit "Create" %>
<% end %>
```

## ViewComponent compatibility

Theo is compatible with [ViewComponent](https://viewcomponent.org/). For here's a component using Theo template syntax:

```
class ExampleComponent < ViewComponent::Base
theo_template <<-THEO
<%= content %>
THEO

def initialize(title:)
@title = title
end
end
```
It can be rendered as usual from another Theo template:
```
<%= render(ExampleComponent.new(title: "my title")) do %>
Hello, World!
<% end %>
```
(It will be simplifid soon, e.g. via ``)