Using Classnames with Alpine for a dropdown

by philw_

HTML

<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/tailwindcss/1.4.5/tailwind.min.css">
<script src="https://cdn.jsdelivr.net/gh/ctidigital/outline.js@1.3.0/outline.js"></script>
<script src="https://cdn.jsdelivr.net/npm/ally.js@1.4.1/ally.min.js"></script>
<!-- Classnames isn't needed in Alpine v2 but might be in v3 -->
<script
  src="https://cdn.jsdelivr.net/npm/classnames@2.2.6/index.js"
  integrity="sha256-rrq9qHuIVReH5epxJgE6YObA8I1zK7lcx5ZUBZrwMXE="
  crossorigin="anonymous"
></script>
<script
  src="https://cdn.jsdelivr.net/npm/alpinejs@2.3.3/dist/alpine.js"
  integrity="sha256-YMtt6o4gwAMeWbjhX1r2n/ZgaCCA7BEKCRsLVN+qeDA="
  crossorigin="anonymous"
></script>

<main class="space-y-8">
  <div
    x-data="{ 
      open: false,
      tabConstraint: null
    }"
    class="relative dropdown max-w-sm"
    x-on:keydown.escape="open = false"
    x-init="
      // In real life I'd put this in a .js file to benefit from
      // Prettier, linting, minifying etc, but it's _possible_ to
      // do it all inside your markup.
      $watch('open', (open) => {
        $dispatch('toggle-overlay', { state: open });
        $nextTick(() => {
          tabConstraint = open
            ? ally.maintain.tabFocus({ context: $el })
            : tabConstraint.disengage();
          if (open) { $refs.firstLink.focus(); }
        });
      });
    "
  >
    <button
      id="dropdown-button"
      @click="open = true"
      class="w-full flex font-bold p-5"
      :class="classNames(
        {
          'bg-red-500 rounded-t-lg relative z-10': open,
          'bg-gray-300 hover:bg-gray-400 focus:bg-gray-400 rounded-lg': !open
        }
      )"
      aria-haspopup="true"
      :aria-expanded="open"
    >
      <div class="flex-grow">
        <span x-text="!open ? 'Open' : 'Hide'">Open</span> dropdown
      </div>

      <div class="w-6">
        <div x-show="!open" data-feather="menu"></div>
        <div x-show="open" data-feather="x"></div>
      </div>
   ...

SCSS

[x-cloak] { display: none; }

p {
  a {
    text-decoration: underline;

    &:hover,
    &:focus {
      color: #2b6cb0;
    }
  }
}

JavaScript

// No JS here!