# Language Tutorial

This page contains a tutorial covering the essential parts of the hop language.

All of the code examples on this page are interactive. You can click the **Open in
playground** buttons on the bottom of the code blocks to run the code in your
browser.

Let's get started!

## Views

Views are the entry points for a hop program.

Every view in a program compiles to a function that is callable from Rust.
The simplest possible program is one that declares a single view.

```hop
view Greeting(name: String) {
  <div>
    Hello {name}!
  </div>
}
```

Any describable type in hop can be used as the type of a view parameter,
allowing you to pass data from the host program in a fully type-safe manner.

In Rust, we would render the `Greeting` view like this:
```rust
mod hop;

fn main() {
  let view = hop::Greeting {
    name: "World".to_string(),
  };

  println!("{}", view.render());
}
```

When compiling the view, hop will add the necessary boilerplate to turn
your view into a complete HTML document.

```html
<!doctype html>
<html>
<head>
  <meta charset="utf-8">
  <meta content="width=device-width, initial-scale=1" name="viewport">
</head>
<body>
  <div>Hello World!</div>
</body>
</html>
```

Let's move on to the next topic, components!

## Components

Components are the building blocks of a hop program.

Components make it possible to construct composable user interfaces. This
concept might be familiar to you if you've ever tried React, Vue,
Blazor, Flutter or similar frameworks.

The syntax for declaring a component looks a lot like the syntax for declaring
a view. However, unlike views, components can be invoked in views and in
other components.

```hop
// The AlbumCard component renders an album with its cover art.
component AlbumCard(
  cover: String,
  title: String,
  author: String,
  year: Int,
) {
  <div>
    <img src={cover}>
    <h2>
      {title}
    </h2>
    <div>
      Released {year.to_string()} by {author}
    </div>
  </div>
}

// In the Main view we render three instances
// of the AlbumCard component.
view Main {
  <div>
    <AlbumCard
      cover="/tutorial/2012_2017.jpg"
      title="2012-2017"
      author="Against All Logic"
      year={2018}
    />
    <AlbumCard
      cover="/tutorial/inner_song.jpg"
      title="Inner Song"
      author="Kelly Lee Owens"
      year={2020}
    />
    <AlbumCard
      cover="/tutorial/persona.jpg"
      title="Persona"
      author="Rival Consoles"
      year={2018}
    />
  </div>
}
```

### Styling components

To style our components we'll use the CSS framework Tailwind.

hop has built-in support for Tailwind so we can get started by
just adding Tailwind classes directly to the `class` attribute of
HTML elements in the code.

If you've never used Tailwind before we recommend skimming [their introduction](https://tailwindcss.com/docs/styling-with-utility-classes)
before continuing.

Now, let's add some styling.

```hop
component AlbumCard(
  cover: String,
  title: String,
  author: String,
  year: Int,
) {
  <div class={
    join!(
      "flex",
      "border",
      "rounded-lg",
      "overflow-hidden",
      "sm:flex-col",
    )
  }>
    <img src={cover} class="h-24 sm:h-auto">
    <div class={
      join!(
        "flex",
        "flex-col",
        "gap-1",
        "p-5",
      )
    }>
      <h2 class="text-lg sm:text-xl">
        {title}
      </h2>
      <span class="text-zinc-600">
        Released {year.to_string()} by {author}
      </span>
    </div>
  </div>
}

view Main {
  <div class={
    join!(
      "max-w-5xl",
      "mx-auto",
      "p-8",
      "flex",
      "flex-col",
      "gap-4",
      "sm:flex-row",
    )
  }>
    <AlbumCard
      cover="/tutorial/2012_2017.jpg"
      title="2012-2017"
      author="Against All Logic"
      year={2018}
    />
    <AlbumCard
      cover="/tutorial/inner_song.jpg"
      title="Inner Song"
      author="Kelly Lee Owens"
      year={2020}
    />
    <AlbumCard
      cover="/tutorial/persona.jpg"
      title="Persona"
      author="Rival Consoles"
      year={2018}
    />
  </div>
}
```

If you've seen Tailwind before the additions above will probably look mostly familiar to you.

One thing that might stick out is the use of the `join!` macro inside the `Main` view
containing the CSS classes broken up into separate strings.

The `join!` macro concatenates strings, adding a space between each string while filtering out empty strings.

In hop, it is idiomatic to use this macro to break up class strings.
This aids readability and allows for better diffs in version control systems.

The `join!` macro is evaluated at compile time and adds no runtime overhead so it is recommended to use it generously.
The hop formatter will also break up and format the strings for you automatically if you write
`join!("foo bar baz")`.

## Building flexible components

Designing flexible components that are easy to reuse across different contexts of a user interface is hard.
Just like in all software design, spending some time thinking about how components will be
used before establishing their API usually pays off in the long run.

### Modules and visibility

One of the most fundamental tools in software design is encapsulation, the ability
to decide whether code and data should be public or private in a given context.

In hop, any declaration (such as a component declaration or a type declaration)
can either be private to the file they are declared in, or public to the whole
program.

By default, all declarations are *private* and can only be used in the file
they are declared in. To make a declaration *public*, add the keyword `pub` to
the start of a declaration. When a declaration is made public, it can be
imported from another file using the `import` keyword.

::: code-group

```hop [main.hop]
import icons::DownloadIcon
import icons::GhostIcon
import icons::HeartIcon

view Main {
  <div class={
    join!(
      "flex",
      "gap-4",
      "justify-center",
      "items-center",
      "py-16",
    )
  }>
    <DownloadIcon/>
    <GhostIcon/>
    <HeartIcon/>
  </div>
}
```

```hop [icons.hop]
pub component DownloadIcon {
  <svg
    xmlns="http://www.w3.org/2000/svg"
    width="24"
    height="24"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
  >
    <path d="M12 15V3">
    </path>
    <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4">
    </path>
    <path d="m7 10 5 5 5-5">
    </path>
  </svg>
}

pub component GhostIcon {
  <svg
    xmlns="http://www.w3.org/2000/svg"
    width="24"
    height="24"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
    class="lucide lucide-ghost-icon lucide-ghost"
  >
    <path d="M9 10h.01">
    </path>
    <path d="M15 10h.01">
    </path>
    <path d="M12 2a8 8 0 0 0-8 8v12l3-3 2.5 2.5L12 19l2.5 2.5L17 19l3 3V10a8 8 0 0 0-8-8z">
    </path>
  </svg>
}

pub component HeartIcon {
  <svg
    xmlns="http://www.w3.org/2000/svg"
    width="24"
    height="24"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
    class="lucide lucide-heart-icon lucide-heart"
  >
    <path d="M2 9.5a5.5 5.5 0 0 1 9.591-3.676.56.56 0 0 0 .818 0A5.49 5.49 0 0 1 22 9.5c0 2.29-1.5 4-3 5.5l-5.492 5.313a2 2 0 0 1-3 .019L5 15c-1.5-1.5-3-3.2-3-5.5">
    </path>
  </svg>
}
```

:::

The ability to make components private makes it possible to break up complex
components into sub-components without changing the public interface that a file
exports.

Now, let's look in to component composition.

### Slots and composition

To make composition of components possible, hop allows for components to
declare a *slot* which allows the invoker to render markup inside the slot. The
parameter name of a slot must be declared as `slot` (lowercase) and its type
must be `Slot` (capitalized).

::: code-group

```hop [main.hop]
import icons::DownloadIcon

component Button(slot: Slot) {
  <button class={
    join!(
      "h-9",
      "px-4",
      "border",
      "inline-flex",
      "items-center",
      "justify-center",
      "gap-2",
      "rounded-full",
      "text-sm",
      "font-medium",
      "shrink-0",
    )
  }>
    {slot}
  </button>
}

view Main {
  <div class={
    join!(
      "flex",
      "justify-center",
      "py-16",
    )
  }>
    <!--
      The content inside <Button> is evaluated
      in this variable scope, but rendered in the
      slot of the Button
    -->
    <Button>
      Download
      <DownloadIcon/>
    </Button>
  </div>
}
```

```hop [icons.hop]
pub component DownloadIcon {
  <svg
    xmlns="http://www.w3.org/2000/svg"
    width="24"
    height="24"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
    class="size-4"
  >
    <path d="M12 15V3">
    </path>
    <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4">
    </path>
    <path d="m7 10 5 5 5-5">
    </path>
  </svg>
}
```

:::

The fact that a component can have at most one slot might seem like a serious
limitation, but we can leverage the slot as a building block to create
complex structures. We will look into that now.

### Compound component pattern

The compound component pattern is, in some sense, a way to create a component
structure that can have multiple slots. In this pattern we create a hierarchy
of components that should be used together.

It is recommended to let all components that are part of the same hierarchy
share a prefix in their name. If it makes sense, let the name of the
outermost component be just the prefix.

::: code-group

```hop [main.hop]
import alert::Alert
import alert::AlertTitle 
import alert::AlertDescription 
import icons::CircleCheckIcon

view Main {
  <div class="p-6">
    <Alert>
      <CircleCheckIcon/>
      <AlertTitle>
        Upload successful
      </AlertTitle>
      <AlertDescription>
        Your package has successfully been uploaded to the repository.
      </AlertDescription>
    </Alert>
  </div>
}
```

```hop [alert.hop]
pub component Alert(
  slot: Slot,
  class: String = "",
) {
  <div
    data-slot="alert"
    role="alert"
    class={
      join!(
        "grid",
        "w-full",
        "gap-0.5",
        "rounded-lg",
        "border",
        "border-neutral-300",
        "px-2.5",
        "py-2",
        "text-left",
        "text-sm",
        "has-[>svg]:grid-cols-[auto_1fr]",
        "has-[>svg]:gap-x-2",
        "*:[svg]:row-span-2",
        "*:[svg]:translate-y-0.5",
        "*:[svg]:text-current",
        "*:[svg:not([class*='size-'])]:size-4",
        "bg-white",
        class,
      )
    }
  >
    {slot}
  </div>
}

pub component AlertTitle(
  slot: Slot,
  class: String = "",
) {
  <div
    data-slot="alert-title"
    class={
      join!(
        "font-medium",
        class,
      )
    }
  >
    {slot}
  </div>
}

pub component AlertDescription(
  slot: Slot,
  class: String = "",
) {
  <div
    data-slot="alert-description"
    class={
      join!(
        "text-sm",
        "text-balance",
        "text-neutral-600",
        class,
      )
    }
  >
    {slot}
  </div>
}

```

```hop [icons.hop]
pub component CircleCheckIcon(class: String = "") {
  <svg
    xmlns="http://www.w3.org/2000/svg"
    width="24"
    height="24"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
    class={class}
  >
    <circle cx="12" cy="12" r="10">
    </circle>
    <path d="m9 12 2 2 4-4">
    </path>
  </svg>
}
```

:::

### Component variant pattern

It is often useful to be able to define different variants of a component that
share a common base style.

In hop, the idiomatic way to achieve this is to declare the variants as an
enum, and then use `match` expressions to handle each case. The `match`
expression in hop is similar to the one in Rust. It is always exhaustive,
meaning that the compiler will tell you if you've forgot to handle a case.

::: code-group

```hop [main.hop]
import icons::DownloadIcon

enum ButtonSize {
  Sm,
  Default,
  Lg,
}

component Button(
  slot: Slot,
  size: ButtonSize = ButtonSize::Default,
) {
  <button class={
    join!(
      "border",
      "inline-flex",
      "items-center",
      "justify-center",
      "gap-2",
      "rounded-full",
      "text-sm",
      "font-medium",
      "shrink-0",
      match size {
        ButtonSize::Sm => join!(
          "h-8",
          "px-3",
          "gap-1.5",
        ),
        ButtonSize::Default => join!(
          "h-9",
          "px-4",
        ),
        ButtonSize::Lg => join!(
          "h-10",
          "px-6",
        ),
      },
    )
  }>
    {slot}
  </button>
}

view Main {
  <div class={
    join!(
      "flex",
      "gap-4",
      "justify-center",
      "items-center",
      "py-16",
    )
  }>
    <!-- Render small button -->
    <Button size={ButtonSize::Sm}>
      Download
      <DownloadIcon/>
    </Button>
    <!-- Render default button -->
    <Button>
      Download
      <DownloadIcon/>
    </Button>
    <!-- Render large button -->
    <Button size={ButtonSize::Lg}>
      Download
      <DownloadIcon/>
    </Button>
  </div>
}

```

```hop [icons.hop]
pub component DownloadIcon {
  <svg
    xmlns="http://www.w3.org/2000/svg"
    width="24"
    height="24"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
    class="size-4"
  >
    <path d="M12 15V3">
    </path>
    <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4">
    </path>
    <path d="m7 10 5 5 5-5">
    </path>
  </svg>
}
```

:::

### Appending and overriding CSS classes

It can be useful to be able to override or
append to the default styles of a component. To achieve
this we can allow for extra classes to be sent in via
a parameter and added to the arguments of `join!`.

It is idiomatic to call the extra parameter `class` so that the attribute for a
component mirrors the attribute for an HTML element.

::: code-group

```hop [main.hop]
import icons::DownloadIcon

component Button(
  slot: Slot,
  class: String = "",
) {
  <button class={
    join!(
      "h-9",
      "px-4",
      "border",
      "inline-flex",
      "items-center",
      "justify-center",
      "gap-2",
      "rounded-full",
      "text-sm",
      "font-medium",
      "shrink-0",
      class,
    )
  }>
    {slot}
  </button>
}

view Main {
  <div class={
    join!(
      "flex",
      "justify-center",
      "py-16",
    )
  }>
    <Button class={
      join!(
        "hover:scale-110",
        "transition-transform",
      )
    }>
      Download
      <DownloadIcon/>
    </Button>
  </div>
}
```

```hop [icons.hop]
pub component DownloadIcon {
  <svg
    xmlns="http://www.w3.org/2000/svg"
    width="24"
    height="24"
    viewBox="0 0 24 24"
    fill="none"
    stroke="currentColor"
    stroke-width="2"
    stroke-linecap="round"
    stroke-linejoin="round"
    class="size-4"
  >
    <path d="M12 15V3">
    </path>
    <path d="M21 15v4a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-4">
    </path>
    <path d="m7 10 5 5 5-5">
    </path>
  </svg>
}
```

:::

Note that we add a default value to the class parameter of the `Button`
component. This is necessary, since otherwise we would get a compile error if
we didn't provide a value for the `class` attribute when rendering `Button`. Default
values can be specified for component parameters, but not for view parameters.

::: info Advanced: Conflict resolution of Tailwind classes
If you are aware of the the intricacies of HTML/CSS you might be worried about
conflict resolution of Tailwind classes.

The fact that HTML puts no semantic meaning into the order in which classes appear inside
the `class` attribute has spawned various overhead inducing workarounds for component frameworks such as `twMerge` in React.

In hop, conflict resolution is handled automatically at compile time. Since hop is aware of
the semantics of Tailwind it evaluates the value of `class` attributes
and removes the classes that has overrides. The last CSS class of a conflict wins. Therefore it is important that the overrides appear
last inside the join macro or class string, i.e. `join!(..., overrides)`.
:::

### Advanced: Tagged element pattern

Sometimes a component needs to be able to be rendered as different HTML
elements (sometimes called *polymorphic components* in React). The typical
example of this is a `Button` component that needs to be rendered as an `<a>`
element in some contexts and as a `<button>` element in other contexts. In hop,
the idiomatic way to achieve this is via the *tagged element pattern*.

The tagged element pattern works
by declaring an enum for each of the elements that the component can render as. The enum variants declare the
attributes of each element as fields. A base component then uses the `<match>` tag to destructure the
enum and passes the attributes to the respective HTML element in a type-safe manner.

A component that wraps the base component and constructs the enum variant is then defined for each of the
possible HTML elements, making the API more ergonomic for the caller.

::: code-group

```hop [main.hop]
import button::ButtonLink
import button::Button

view Main {
  <div class={
    join!(
      "flex",
      "gap-4",
      "justify-center",
      "items-center",
      "py-16",
    )
  }>
    <!-- Render as <button> -->
    <Button>
      Download
    </Button>
    <!-- Render as <a> -->
    <ButtonLink href="#">
      Download
    </ButtonLink>
  </div>
}
```

```hop [button.hop]
enum ButtonElement {
  Link {
    href: String,
  },
  Button {
    type: String,
  },
}

component ButtonBase(
  slot: Slot,
  el: ButtonElement,
) {
  <let {
    classes: String = join!(
      "h-9",
      "px-4",
      "border",
      "inline-flex",
      "items-center",
      "justify-center",
      "gap-2",
      "rounded-full",
      "text-sm",
      "font-medium",
      "shrink-0",
    ),
  }>
    <match {el}>
      <case {ButtonElement::Link {href}}>
        <a href={href} class={classes}>
          {slot}
        </a>
      </case>
      <case {ButtonElement::Button {type}}>
        <button type={type} class={classes}>
          {slot}
        </button>
      </case>
    </match>
  </let>
}

pub component ButtonLink(
  slot: Slot,
  href: String,
) {
  <ButtonBase el={ButtonElement::Link {href: href}}>
    {slot}
  </ButtonBase>
}

pub component Button(
  slot: Slot,
  type: String = "button",
) {
  <ButtonBase el={ButtonElement::Button {type: type}}>
    {slot}
  </ButtonBase>
}
```

:::

## Asset handling

During compilation hop has the ability to copy assets used in the HTML to an output directory.

To register an asset into the asset pipeline of hop, use the `asset!` macro.

```hop
view Main {
  <img src={asset!("/logo.svg")}>
}
```

By default, hop will add a hash to the resulting filename on disk as well as in the url inside the macro.
