empowered.house ↗

sections/footers.ex

Footer

A page footer with the EmpoweredHouse lockup, three columns of links and a bottom line.

Use it
# in the module that renders the template
use EHVisuals

<div class="h-96 rounded-xl border border-border">
  <.sections_footers animated />
</div>
default
isometric
custom columns
two columns · hover · no glow
light surface
isometric · fade out · still

Props

Prop Type Default What it does
wordmark string "EmpoweredHouse"
tagline string "Software and agents that run your operations." Wraps onto up to three lines of about 24 characters.
columns list [%{links: ["Workflows", "Agents", "Intelligence", "Pricing"], ...}, ...] Up to three `%{title, links}` with up to five links of about 12 characters each.
legal string "© 2026 EmpoweredHouse"
policies list ["Privacy", "Terms"] Short links on the bottom line.
Frame props, shared by every composition
Prop Type Default What it does
animated boolean false Turns motion on. Without it the composition renders in its final, still state.
trigger "mount" | "in-view" | "in-view-repeat" "in-view" When the entrance plays: on page load, once when scrolled into view, or every time it comes back into view.
hover boolean false Enables the hover micro-interactions the composition supports.
glow boolean true The warm incandescent halo behind the composition.
fade_out boolean false Fades the bottom edge into the surface underneath.
isometric boolean false Tilts the composition into the isometric plane of the platform stack.
theme "dark" | "light" "dark" `dark` for the site canvas, `light` for the cream reversed surface.
class string nil Extra classes for the frame.
rest global — Any other HTML attribute, set on the frame.

Source

Two files. Copy them into your app if you would rather own the code than depend on the library.

lib/eh_visuals/sections/footers.ex
footers.ex
defmodule EHVisuals.Sections.Footers do
  @moduledoc """
  A page footer: the EmpoweredHouse lockup, columns of links and a bottom line.
  """

  use EHVisuals.Composition,
    name: "sections/footers",
    title: "Footer",
    description:
      "A page footer with the EmpoweredHouse lockup, three columns of links and a bottom line."

  @columns [
    %{title: "Product", links: ["Workflows", "Agents", "Intelligence", "Pricing"]},
    %{title: "Company", links: ["About", "Customers", "Careers", "Contact"]},
    %{title: "Resources", links: ["Docs", "Changelog", "Security", "Status"]}
  ]

  attr(:wordmark, :string, default: "EmpoweredHouse")

  attr(:tagline, :string,
    default: "Software and agents that run your operations.",
    doc: "Wraps onto up to three lines of about 24 characters."
  )

  attr(:columns, :list,
    default: @columns,
    doc: "Up to three `%{title, links}` with up to five links of about 12 characters each."
  )

  attr(:legal, :string, default: "© 2026 EmpoweredHouse")
  attr(:policies, :list, default: ~w(Privacy Terms), doc: "Short links on the bottom line.")

  attr(:label, :string,
    default:
      "A page footer with the EmpoweredHouse lockup, three columns of links and a legal line"
  )

  frame_attrs()

  @spec sections_footers(map()) :: Phoenix.LiveView.Rendered.t()
  def sections_footers(assigns) do
    shown = Enum.take(assigns.columns, 3)
    # Three columns sit 70 apart; fewer spread over the same width.
    pitch = if length(shown) > 2, do: 70, else: 105

    columns =
      shown
      |> Enum.with_index()
      |> Enum.map(fn {column, index} ->
        %{
          title: column.title,
          links: column.links |> Enum.take(5) |> Enum.with_index(),
          index: index,
          x: 176 + index * pitch
        }
      end)

    tagline = assigns.tagline |> wrap(24) |> Enum.take(3)
    links = columns |> Enum.map(&length(&1.links)) |> Enum.max(fn -> 1 end)
    rule_y = max(58 + (links - 1) * 18, 62 + (length(tagline) - 1) * 14) + 20

    assigns =
      assigns
      |> assign(:cols, columns)
      |> assign(:tagline_lines, Enum.with_index(tagline))
      |> assign(:rule_y, rule_y)
      |> assign(:height, rule_y + 40)

    ~H"""
    <.frame name="sections/footers" label={@label} {frame_opts(assigns)}>
      <svg viewBox={"0 0 400 #{@height}"}>
        <g class="ehv-h-lift">
          <rect class="ehv-card ehv-a-rise" x="0.5" y="0.5" width="399" height={@height - 1} rx="12" />
          <g class="ehv-a-fade ehv-i-1">
            <.mark x="17.4" y="19" size="30" />
            <text class="ehv-t-strong" x="47" y="39" font-size="14">{@wordmark}</text>
          </g>
          <text
            :for={{line, index} <- @tagline_lines}
            class="ehv-t-3 ehv-a-fade ehv-i-2"
            x="24"
            y={62 + index * 14}
            font-size="10.5"
          >
            {line}
          </text>
          <g :for={column <- @cols} class={"ehv-a-rise ehv-i-#{column.index + 2}"}>
            <text class="ehv-mono" x={column.x} y="37" font-size="8">
              {String.upcase(column.title)}
            </text>
            <text
              :for={{item, row} <- column.links}
              class="ehv-t-2 ehv-sections-footers__link"
              x={column.x}
              y={58 + row * 18}
              font-size="10.5"
            >
              {item}
            </text>
          </g>
          <g class="ehv-a-fade ehv-i-6">
            <line class="ehv-line-faint" x1="24" x2="376" y1={@rule_y + 0.5} y2={@rule_y + 0.5} />
            <text class="ehv-mono" x="24" y={@rule_y + 22} font-size="8">
              {String.upcase(@legal)}
            </text>
            <text class="ehv-mono" x="376" y={@rule_y + 22} font-size="8" text-anchor="end">
              {@policies |> Enum.take(3) |> Enum.map_join("  ·  ", &String.upcase/1)}
            </text>
          </g>
        </g>
      </svg>
    </.frame>
    """
  end

  defp wrap(text, limit) do
    text
    |> String.split()
    |> Enum.reduce([], fn
      word, [] ->
        [word]

      word, [line | rest] ->
        if String.length(line) + 1 + String.length(word) <= limit,
          do: ["#{line} #{word}" | rest],
          else: [word, line | rest]
    end)
    |> Enum.reverse()
  end
end
assets/css/sections/footers.css
footers.css
/* sections/footers */
.ehv-sections-footers { --ehv-max-w: 440px; }

.ehv--hover .ehv-sections-footers__link { transition: fill 200ms var(--ehv-ease); }
.ehv--hover .ehv-sections-footers__link:hover { fill: var(--ehv-ink); }