Common Elements

Layout

Our guiding principle is that components are responsible for their own internal layout, and should therefore avoid specifying external layout characteristics (such as for example margins on the component itself). Components can and should arrange the elements inside them (using for example margins or gaps), but should optimally not prescribe or interfere with layouts described on higher levels.

Pages

The default page layout requires a <body> element, which should contain a <main> element as its direct descendant. Additionally, the <header>, <aside> and <footer> optional elements are also supported (in any combination). The rendering typically follows the holy grail layout, with the <aside> element placed to the right according to the golden ratio.

Buttons

Text Buttons

Example

<div class="button-group">
  <!-- Links -->
  <div class="button-group horizontal">
    <a class="button">{{ _('Button') }}</a>
    <a class="button disabled">{{ _('Disabled') }}</a>
    <a class="button active">{{ _('Active') }}</a>
    <a class="button neutral">{{ _('Neutral') }}</a>
    <a class="button danger">{{ _('Danger') }}</a>
  </div>

  <!-- Buttons -->
  <div class="button-group horizontal">
    <button type="button">{{ _('Button') }}</button>
    <button type="button" class="disabled">{{ _('Disabled') }}</button>
    <button type="button" class="active">{{ _('Active') }}</button>
    <button type="button" class="neutral">{{ _('Neutral') }}</button>
    <button type="button" class="danger">{{ _('Danger') }}</button>
  </div>
</div>
<div class="button-group">
  <!-- Links -->
  <div class="button-group horizontal">
    <a class="button">Button</a>
    <a class="button disabled">Disabled</a>
    <a class="button active">Active</a>
    <a class="button neutral">Neutral</a>
    <a class="button danger">Danger</a>
  </div>

  <!-- Buttons -->
  <div class="button-group horizontal">
    <button type="button">Button</button>
    <button type="button" class="disabled">Disabled</button>
    <button type="button" class="active">Active</button>
    <button type="button" class="neutral">Neutral</button>
    <button type="button" class="danger">Danger</button>
  </div>
</div>

Icon Buttons

Example

<div class="button-group horizontal">
  <!-- Icon-only button -->
  <button type="button" class="icon">
    {{ include_static('django_logikal/icons/x.svg') }}
  </button>

  <!-- Icon-only toggle -->
  <button type="button"
          class="icon toggle"
          title="{{ _('Show password') }}"
          aria-label="{{ _('Show password') }}"
          data-active-title="{{ _('Hide password') }}"
          data-active-aria-label="{{ _('Hide password') }}">
    <span class="inactive" aria-hidden="false">
      {{ include_static('django_logikal/icons/eye-off.svg') }}
    </span>
    <span class="active" aria-hidden="true">
      {{ include_static('django_logikal/icons/eye.svg') }}
    </span>
  </button>

  <!-- Icon and text button -->
  <button type="button" class="icon text">
    {{ include_static('django_logikal/icons/x.svg') }}
    <span>{{ _('Close') }}</span>
  </button>
</div>
<div class="button-group horizontal">
  <!-- Icon-only button -->
  <button type="button" class="icon">
    <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-x preview-icon">
      <path d="M18 6 6 18" />
      <path d="m6 6 12 12" />
    </svg>
  </button>

  <!-- Icon-only toggle -->
  <button
    type="button"
    class="icon toggle"
    title="Show password"
    aria-label="Show password"
    data-active-title="Hide password"
    data-active-aria-label="Hide password">
    <span class="inactive" aria-hidden="false">
      <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-eye-off preview-icon">
        <path
          d="M10.733 5.076a10.744 10.744 0 0 1 11.205 6.575 1 1 0 0 1 0 .696 10.747 10.747 0 0 1-1.444 2.49" />
        <path d="M14.084 14.158a3 3 0 0 1-4.242-4.242" />
        <path
          d="M17.479 17.499a10.75 10.75 0 0 1-15.417-5.151 1 1 0 0 1 0-.696 10.75 10.75 0 0 1 4.446-5.143" />
        <path d="m2 2 20 20" />
      </svg>
    </span>
    <span class="active" aria-hidden="true">
      <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-eye preview-icon">
        <path
          d="M2.062 12.348a1 1 0 0 1 0-.696 10.75 10.75 0 0 1 19.876 0 1 1 0 0 1 0 .696 10.75 10.75 0 0 1-19.876 0" />
        <circle cx="12" cy="12" r="3" />
      </svg>
    </span>
  </button>

  <!-- Icon and text button -->
  <button type="button" class="icon text">
    <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-x preview-icon">
      <path d="M18 6 6 18" />
      <path d="m6 6 12 12" />
    </svg>
    <span>Close</span>
  </button>
</div>

Dialogs

Popover

Example

<div class="example-page example-section">
  <div class="popover">Popover</div>
</div>
<div class="example-page example-section">
  <div class="popover">Popover</div>
</div>

Component Macros

template django_logikal/components/commons.html.j

Usage:

{% import 'django_logikal/components/commons.html.j' as commons %}
{% block component_head %}{{ component_head('commons') }}{% endblock %}

Components:

macro commons.desktop_menu(items, request, id='menu-desktop', dropdown_icon='django_logikal/icons/chevron-down.svg')

Render a desktop menu bar.

Parameters:
  • items (list[MenuItem]) – The menu items to use.

  • request (HttpRequest | None) – The HTTP request to use.

  • id (str) – The menu ID to use.

  • dropdown_icon (str) – The path for the dropdown icon to use.

Example

<nav>
  {{ commons.desktop_menu([
    menu_item(title='Home', view_name='main:home'),
    menu_item(title='Info', submenu=[
      menu_item(title='Home', id='submenu-home', view_name='main:home'),
      menu_item(title='About', view_name='main:about'),
      menu_item(title='Components', submenu=[
        menu_item(title='Home', id='subsubmenu-home', view_name='main:home'),
        menu_item(title='Buttons', view_name='main:buttons'),
      ]),
    ]),
    menu_item(title='Blog', submenu=[
      menu_item(title='Newest', view_name='main:blog', view_kwargs={'order': 'desc'}),
      menu_item(title='Oldest', view_name='main:blog', view_kwargs={'order': 'asc'}),
    ]),
  ], request=request) }}
</nav>
<nav>
  <menu role="menubar" id="menu-desktop" class="desktop">
    <li role="none">
      <a
        role="menuitem"
        id="menu-desktop-home"
        class="button disabled"
        aria-current="page">
        Home
      </a>
    </li>
    <li role="none">
      <button
        type="button"
        class="icon dropdown"
        role="menuitem"
        id="menu-desktop-info"
        aria-haspopup="menu"
        aria-expanded="false">
        Info
        <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-chevron-down preview-icon">
          <path d="m6 9 6 6 6-6" />
        </svg>
      </button>
      <menu role="menu" class="popover">
        <li role="none">
          <a
            role="menuitem"
            id="menu-desktop-submenu-home"
            class="button disabled"
            aria-current="page">
            Home
          </a>
        </li>
        <li role="none">
          <a
            role="menuitem"
            id="menu-desktop-about"
            class="button"
            href="main/about//">
            About
          </a>
        </li>
        <li role="none">
          <button
            type="button"
            class="icon dropdown horizontal"
            role="menuitem"
            id="menu-desktop-components"
            aria-haspopup="menu"
            aria-expanded="false">
            Components
            <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-chevron-down preview-icon">
              <path d="m6 9 6 6 6-6" />
            </svg>
          </button>
          <menu role="menu" class="popover">
            <li role="none">
              <a
                role="menuitem"
                id="menu-desktop-subsubmenu-home"
                class="button disabled"
                aria-current="page">
                Home
              </a>
            </li>
            <li role="none">
              <a
                role="menuitem"
                id="menu-desktop-buttons"
                class="button"
                href="main/buttons//">
                Buttons
              </a>
            </li>
          </menu>
        </li>
      </menu>
    </li>
    <li role="none">
      <button
        type="button"
        class="icon dropdown"
        role="menuitem"
        id="menu-desktop-blog"
        aria-haspopup="menu"
        aria-expanded="false">
        Blog
        <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-chevron-down preview-icon">
          <path d="m6 9 6 6 6-6" />
        </svg>
      </button>
      <menu role="menu" class="popover">
        <li role="none">
          <a
            role="menuitem"
            id="menu-desktop-newest"
            class="button"
            href="main/blog/order/desc/">
            Newest
          </a>
        </li>
        <li role="none">
          <a
            role="menuitem"
            id="menu-desktop-oldest"
            class="button"
            href="main/blog/order/asc/">
            Oldest
          </a>
        </li>
      </menu>
    </li>
  </menu>
</nav>

macro commons.mobile_menu(items, request, id='menu-mobile', dropdown_icon='django_logikal/icons/chevron-down.svg', menu_icon='django_logikal/icons/menu.svg', menu_close_icon='django_logikal/icons/x.svg')

Render a mobile menu.

Parameters:
  • items (list[MenuItem]) – The menu items to use.

  • request (HttpRequest | None) – The HTTP request to use.

  • id (str) – The menu ID to use.

  • dropdown_icon (str) – The path for the dropdown icon to use.

  • menu_icon (str) – The path for the menu icon to use.

  • menu_close_icon (str) – The path for the menu close icon to use.

Example

<nav>
  {{ commons.mobile_menu([
    menu_item(title='Home', view_name='main:home'),
    menu_item(title='Info', submenu=[
      menu_item(title='Home', id='submenu-home', view_name='main:home'),
      menu_item(title='About', view_name='main:about'),
      menu_item(title='Components', submenu=[
        menu_item(title='Home', id='subsubmenu-home', view_name='main:home'),
        menu_item(title='Buttons', view_name='main:buttons'),
      ]),
    ]),
    menu_item(title='Blog', submenu=[
      menu_item(title='Newest', view_name='main:blog', view_kwargs={'order': 'desc'}),
      menu_item(title='Oldest', view_name='main:blog', view_kwargs={'order': 'asc'}),
    ]),
  ], request=request) }}
</nav>
<nav>
  <div class="menu-mobile-container">
    <button
      class="icon toggle"
      id="menu-mobile-toggle"
      type="button"
      title="Show menu"
      aria-label="Show menu"
      data-active-title="Hide menu"
      data-active-aria-label="Hide menu"
      aria-haspopup="true"
      aria-controls="menu-mobile"
      aria-expanded="false">
      <span class="inactive" aria-hidden="false"
        ><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-menu preview-icon">
          <path d="M4 5h16" />
          <path d="M4 12h16" />
          <path d="M4 19h16" /></svg
      ></span>
      <span class="active" aria-hidden="true"
        ><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-x preview-icon">
          <path d="M18 6 6 18" />
          <path d="m6 6 12 12" /></svg
      ></span>
    </button>
    <menu id="menu-mobile" role="menubar" class="mobile popover">
      <li role="none">
        <a
          role="menuitem"
          id="menu-mobile-home"
          class="button disabled"
          aria-current="page">
          Home
        </a>
      </li>
      <li role="none">
        <button
          type="button"
          class="icon dropdown"
          role="menuitem"
          id="menu-mobile-info"
          aria-haspopup="menu"
          aria-expanded="false">
          Info
          <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-chevron-down preview-icon">
            <path d="m6 9 6 6 6-6" />
          </svg>
        </button>
        <menu role="menu" class="popover">
          <li role="none">
            <a
              role="menuitem"
              id="menu-mobile-submenu-home"
              class="button disabled"
              aria-current="page">
              Home
            </a>
          </li>
          <li role="none">
            <a
              role="menuitem"
              id="menu-mobile-about"
              class="button"
              href="main/about//">
              About
            </a>
          </li>
          <li role="none">
            <button
              type="button"
              class="icon dropdown"
              role="menuitem"
              id="menu-mobile-components"
              aria-haspopup="menu"
              aria-expanded="false">
              Components
              <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-chevron-down preview-icon">
                <path d="m6 9 6 6 6-6" />
              </svg>
            </button>
            <menu role="menu" class="popover">
              <li role="none">
                <a
                  role="menuitem"
                  id="menu-mobile-subsubmenu-home"
                  class="button disabled"
                  aria-current="page">
                  Home
                </a>
              </li>
              <li role="none">
                <a
                  role="menuitem"
                  id="menu-mobile-buttons"
                  class="button"
                  href="main/buttons//">
                  Buttons
                </a>
              </li>
            </menu>
          </li>
        </menu>
      </li>
      <li role="none">
        <button
          type="button"
          class="icon dropdown"
          role="menuitem"
          id="menu-mobile-blog"
          aria-haspopup="menu"
          aria-expanded="false">
          Blog
          <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-chevron-down preview-icon">
            <path d="m6 9 6 6 6-6" />
          </svg>
        </button>
        <menu role="menu" class="popover">
          <li role="none">
            <a
              role="menuitem"
              id="menu-mobile-newest"
              class="button"
              href="main/blog/order/desc/">
              Newest
            </a>
          </li>
          <li role="none">
            <a
              role="menuitem"
              id="menu-mobile-oldest"
              class="button"
              href="main/blog/order/asc/">
              Oldest
            </a>
          </li>
        </menu>
      </li>
    </menu>
  </div>
</nav>

macro commons.language_switcher(current_language_code, action_url, csrf_input, id='language-switcher', text=None, available_languages=None, dropdown_icon='django_logikal/icons/chevron-down.svg')

Render a language switcher.

Parameters:
  • current_language_code (str) – The current language code.

  • action_url (str) – The action URL to use.

  • csrf_input (str) – The CSRF input element to use.

  • id (str) – The ID to use for the menu element.

  • text (str) – The button text to use. Defaults to the current language name.

  • available_languages (list[tuple[str, str]]) – A list of available language code, language name pairs. Defaults to the configured languages.

  • dropdown_icon (str) – The path for the dropdown icon to use.

Example

<nav>
  {{ commons.language_switcher(
    current_language_code=language(),
    action_url=url('set_language'),
    csrf_input=csrf_input
  ) }}
</nav>
<nav>
  <menu class="language-switcher" id="language-switcher" role="menu">
    <li role="none">
      <button
        type="button"
        id="language-switcher-toggle"
        class="icon dropdown"
        role="menuitem"
        title="Change language"
        aria-label="Change language"
        aria-haspopup="menu"
        aria-expanded="false"
        aria-controls="language-switcher-form">
        English (US)
        <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-chevron-down preview-icon">
          <path d="m6 9 6 6 6-6" />
        </svg>
      </button>
      <form id="language-switcher-form" action="set_language/" method="post">
        <menu role="menu" class="popover">
          <li role="none">
            <button name="language" value="en-gb" type="submit" role="menuitem">
              English (GB)
            </button>
          </li>
        </menu>
      </form>
    </li>
  </menu>
</nav>

macro commons.messages(messages, id='messages')

Show messages as a popover notification.

Parameters:
  • messages (list[str] | None) – A list of messages to show.

  • id (str) – The element ID to use.

Example

<div class="example-page">
  {{ commons.messages(messages|default(none)) }}
</div>
<div class="example-page">
  <div id="messages" class="popover notification">
    <ul>
      <li>Notification message.</li>
    </ul>
    <button id="messages-dismiss" type="button" class="active">Dismiss</button>
  </div>
</div>

class django_logikal.components.commons.MenuItem(title: str, view_name: str | None = None, view_kwargs: dict[str, ~typing.Any] = <factory>, submenu: list[~django_logikal.components.commons.MenuItem] = <factory>, id: str | None = None)

An item in a menu.