# LiveView 0.18 Features for Improved Accessibility

In this post, we’ll take a look at the latest LiveView 0.18 features that improve accessibility by enhancing focus. We’ll explore these features through practical examples, so you can see how they work in real-world scenarios. Fly.io is a great place to run your Phoenix LiveView applications! Check out how to [get started](https://docs/elixir/ "").

In [previous](/content/blog/accessibility-clearing-the-fog/ ""/index.html) posts, Nolan showed us some ways to improve accessibility in existing web applications using the Phoenix real-time social music app [LiveBeats](/content/blog/livebeats/ ""/index.html) as an example.

But what if we could integrate accessibility practices into our app development from the beginning, easily? Well, LiveView 0.18 recognizes the importance of accessibility and introduces a new range of built-in primitives that help us manage focus for more accessible LiveView apps, including [Phoenix.Component.focus_wrap/1](https://hexdocs.pm/phoenix_live_view/Phoenix.Component.html#focus_wrap/1 ""), [JS.focus](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.JS.html#focus/0 ""), [JS.focus_first](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.JS.html#focus_first/0 ""), [JS.push_focus](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.JS.html#push_focus/0 ""), and [JS.pop_focus](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.JS.html#pop_focus/0 "").

Today, we’ll explore these primitives with practical examples.

## Defining a Navigation Bar

You are designing a navigation bar and have included default focusable tags, allowing users to navigate between its elements using the tab key. Additionally, it has incorporated a dropdown menu with submenus that can also be accessed using the keyboard:

While our nav bar appears to be functional, there are still a few details that require attention:

1. The dropdown should [focus on the first available option](/content/phoenix-files/liveview-accessible-focus/#hejyw-focusing-the-first-element-inside-a-container ""/index.html) when opened.
2. The navbar element that was in focus prior to displaying the dropdown [should regain focus when the dropdown is closed.](/content/phoenix-files/liveview-accessible-focus/#h7uls-focus-an-specific-element ""/index.html)
3. To improve usability, only the list items in the dropdown should be [focusable when it is opened](/content/phoenix-files/liveview-accessible-focus/#hykzs-manipulating-focus-programmatically "/index.html).

To address these issues, let’s take a look at the dropdown code:

```elixir
attr :id, :any, required: true

slot :header

def dropdown(assigns) do
  ~H"""
  <!-- Dropdown header -->
  <button id={@id}>
    <%= render_slot(@header) %>
    ...
  </button>

<!-- Dropdown body -->
  <div  id={"#{@id}-body"}>
    <ul id={"#{@id}-options"}>
      <li :for={option <- @option}>
        <.link>
          <%= render_slot(option) %>
        </.link>
      </li>
    </ul>
   </div>
  """
end
```

The dropdown component has two main sections: the header, which is a button that displays the dropdown options, and the body, which contains the dropdown options themselves.

### Focusing the First Element Inside a Container

Let’s focus on the button that displays the dropdown options.

```elixir
def dropdown(assigns) do
  ~H"""
  <!-- Dropdown header -->
  <button id={@id} phx-click={open_dropdown(@id)}>
    <%= render_slot(@header) %>
    ...
  </button>

<!-- Dropdown body -->
    ...
  """
end
```

Then we define the function `open_dropdown/2`:

```elixir
def open_dropdown(js \ %JS{}, id) when is_binary(id) do
  js
  |> JS.show(
      to: "##{id}-body",
      transition:
        {"transition-all transform ease-out duration-300",
         "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95",
         "opacity-100 translate-y-0 sm:scale-100"}
      )
  |> JS.focus_first(to: "##{id}-options")
end
```

To begin, we’ll use the [JS.show/1](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.JS.html#show/1 "") command to display the dropdown options container and then use the new [JS.focus_first/1](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.JS.html#focus_first/1 "") command to set focus on the first element within the `<ul>` tag.

### Focus a Specific Element

Now let’s address the second issue, which is to set focus on the last element that was focused before the dropdown was opened.

```elixir
def dropdown(assigns) do
  ~H"""
  <!-- Dropdown header -->
   ...

<!-- Dropdown body -->
  <div  id={"#{@id}-body"}>
    <ul id={"#{@id}-options"}>
      <li :for={option <- @option}>
        <.link phx-keydown={close_dropdown(@id)} phx-key="escape">
          <%= render_slot(option) %>
        </.link>
      </li>
    </ul>
   </div>
  """
end
```

We use `:phx-keydown` and `:phx-key`, to specify that the `close_dropdown/2` function is called when the user presses the escape key.

Next, let’s look at the `close_dropdown/2` function:

```elixir
def close_dropdown(js \ %JS{}, id) do
  js
  |> JS.hide(
    to: "##{id}-body",
    time: 200,
    transition:
      {"transition-all transform ease-in duration-200",
       "opacity-100 translate-y-0 sm:scale-100",
       "opacity-0 translate-y-4 sm:translate-y-0 sm:scale-95"}
  )
  |> JS.focus(to: "##{id}")
end
```

We use the [JS.hide/1](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.JS.html#hide/1 "") command to hide the dropdown body, followed by the [JS.focus/1](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.JS.html#focus/1 "") command with the `id` of the component’s header to focus the dropdown button.

### Wrap the Focused Tab Inside a Container

When the dropdown menu is open and we finish navigating its options, the focus shifts to the navigation bar instead of remaining within the dropdown. To prevent this from happening, we need to ensure that the focus remains inside the dropdown while it is open.

The solution is simple. In LiveView 0.18, a new function component called `focus_wrap/1` was introduced, which allows us to wrap the focus tab within a single container.

```elixir
def dropdown(assigns) do
  ~H"""
  <!-- Dropdown header -->
   ...

<!-- Dropdown body -->
  <.focus_wrap id={"#{@id}-body"}>
    <ul id={"#{@id}-options"}>
      <li :for={option <- @option}>
        <.link phx-keydown={close_dropdown(@id)} phx-key="escape">
          <%= render_slot(option) %>
        </.link>
      </li>
    </ul>
   </.focus_wrap>
  """
end
```

### Changing Focus Programmatically

In addition to the previous commands, there are a couple more commands that we can use to move and activate the focus at appropriate times: [JS.push_focus/2](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.JS.html#push_focus/2 "") and [JS.pop_focus/0](https://hexdocs.pm/phoenix_live_view/Phoenix.LiveView.JS.html#pop_focus/0 "").

To better understand these commands, let’s consider an example scenario. Suppose you have a button that opens a modal to delete an item from a table. The modal presents two options - either delete the element or cancel the deletion process by pressing the Cancel button:

```elixir
<.link
  id={"delete-user-#{user.id}"
  phx-click={show_modal("delete-modal-#{user.id}") |> JS.push_focus()}
>
  <Heroicons.trash fill="red" stroke="white" />
</.link>
```

When the user clicks on this button, it not only displays the modal but it also pushes the focus to itself using the `JS.push_focus/0` command.

Next, when the user clicks the Cancel button within the modal, we can close the modal using the appropriate commands and use the `JS.pop_focus/0` command to activate the focus on the previously focused element:

```elixir
<.button phx-click={hide_modal(@on_cancel, @id) |> JS.pop_focus}>
  Cancel
</.button>
```

By using these two commands, we are able to move the focus and activate it in two separate steps.

### Discussion

LiveView’s focus navigation commands provide a powerful tool to improve the accessibility and user experience of web applications. By using these commands, we can ensure that the focus is correctly managed and activated, allowing users to navigate through our app with ease. Whether it’s navigating through dropdown menus or managing modal dialog boxes, LiveView’s focus navigation commands provide an intuitive and reliable way to keep our users happy. So why not give them a try and see how they can improve your app’s accessibility and user experience?
