empowered.house ↗

integrations/logo-orbit.ex

Logo orbit

System tiles orbiting the EmpoweredHouse mark on two rings.

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

<div class="h-96 rounded-xl border border-border">
  <.integrations_logo_orbit animated />
</div>
default
isometric
fewer systems · one highlighted
full rings · hover
light surface
isometric · fade out · still

Props

Prop Type Default What it does
inner list ["ERP", "CRM", "Mail", "Sheets"] Two to five system names on the inner ring, six characters at most.
outer list ["Bank", "WMS", "HR", "BI", "Chat", "Docs"] Three to eight system names on the outer ring, six characters at most.
highlight any nil The name of one system to outline in lime, or `nil` for none.
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/integrations/logo-orbit.ex
logo-orbit.ex
defmodule EHVisuals.Integrations.LogoOrbit do
  @moduledoc """
  System tiles on two rings around the EmpoweredHouse mark. The rings turn
  slowly in opposite directions and every tile stays upright.
  """

  use EHVisuals.Composition,
    name: "integrations/logo-orbit",
    title: "Logo orbit",
    description: "System tiles orbiting the EmpoweredHouse mark on two rings."

  @cx 150
  @cy 150
  @inner_r 66
  @outer_r 124

  # Glyphs are drawn in a 12 by 12 box with a 1.25 stroke.
  @glyphs %{
    "ERP" => "M1.5 1.5h9v9h-9z M6 1.5v9 M1.5 6h9",
    "CRM" =>
      "M6 5.4a2.2 2.2 0 1 0 0-4.4a2.2 2.2 0 0 0 0 4.4z M1.8 11c0-2.2 1.9-3.6 4.2-3.6s4.2 1.4 4.2 3.6",
    "MAIL" => "M1 2.5h10v7h-10z M1 3l5 4l5-4",
    "SHEETS" => "M1.5 1.5h9v9h-9z M1.5 4.5h9 M1.5 7.5h9 M4.5 1.5v9",
    "BANK" => "M1 4.5l5-3l5 3z M2.5 5v4.5 M6 5v4.5 M9.5 5v4.5 M1 10.5h10",
    "WMS" => "M1.5 3.5l4.5-2l4.5 2v5l-4.5 2l-4.5-2z M1.5 3.5l4.5 2l4.5-2 M6 5.5v5",
    "HR" => "M2 1.5h8v9h-8z M6 5.6a1.4 1.4 0 1 0 0-2.8a1.4 1.4 0 0 0 0 2.8z M3.8 8.4h4.4",
    "BI" => "M2.5 10.5v-4 M6 10.5v-8 M9.5 10.5v-6 M1 10.5h10",
    "CHAT" => "M1.5 2h9v6h-5l-2.5 2.5v-2.5h-1.5z",
    "DOCS" => "M2.5 1.5h5l2 2v7h-7z M4.5 5.5h3 M4.5 7.8h3"
  }
  @fallback_glyph "M6 2.5a3.5 3.5 0 1 0 0 7a3.5 3.5 0 0 0 0-7z"

  attr(:inner, :list,
    default: ~w(ERP CRM Mail Sheets),
    doc: "Two to five system names on the inner ring, six characters at most."
  )

  attr(:outer, :list,
    default: ~w(Bank WMS HR BI Chat Docs),
    doc: "Three to eight system names on the outer ring, six characters at most."
  )

  attr(:highlight, :any,
    default: nil,
    doc: "The name of one system to outline in lime, or `nil` for none."
  )

  attr(:label, :string,
    default: "Ten company systems orbiting the EmpoweredHouse mark on two rings"
  )

  frame_attrs()

  @spec integrations_logo_orbit(map()) :: Phoenix.LiveView.Rendered.t()
  def integrations_logo_orbit(assigns) do
    inner = assigns.inner |> Enum.take(5) |> ring(@inner_r, -45, 2, assigns.highlight)

    outer =
      assigns.outer |> Enum.take(8) |> ring(@outer_r, -90, 2 + length(inner), assigns.highlight)

    assigns =
      assigns
      |> assign(:rings, [{"inner", @inner_r, inner}, {"outer", @outer_r, outer}])
      |> assign(:cx, @cx)
      |> assign(:cy, @cy)

    ~H"""
    <.frame name="integrations/logo-orbit" label={@label} {frame_opts(assigns)}>
      <svg viewBox="0 0 300 300">
        <g class="ehv-h-lift">
          <circle
            :for={{{_name, radius, _tiles}, index} <- Enum.with_index(@rings)}
            class={"ehv-line-soft ehv-a-fade ehv-i-#{index + 1}"}
            cx={@cx}
            cy={@cy}
            r={radius}
          />
          <g
            :for={{name, _radius, tiles} <- @rings}
            class={"ehv-integrations-logo-orbit__ring ehv-integrations-logo-orbit__ring--#{name}"}
          >
            <g :for={tile <- tiles} class="ehv-integrations-logo-orbit__seat">
              <g class={"ehv-a-pop ehv-i-#{tile.index}"}>
                <rect
                  class={[
                    "ehv-card ehv-integrations-logo-orbit__tile",
                    tile.on && "ehv-integrations-logo-orbit__tile--on"
                  ]}
                  x={tile.x - 19}
                  y={tile.y - 19}
                  width="38"
                  height="38"
                  rx="10"
                />
                <path
                  class={["ehv-round", if(tile.on, do: "ehv-line-lime", else: "ehv-line-ink")]}
                  d={tile.glyph}
                  transform={"translate(#{tile.x - 6} #{tile.y - 12.5})"}
                />
                <text
                  class={["ehv-mono", if(tile.on, do: "ehv-t-lime", else: "ehv-t-2")]}
                  x={tile.x}
                  y={tile.y + 12}
                  font-size="6.5"
                  text-anchor="middle"
                >
                  {String.upcase(tile.name)}
                </text>
              </g>
            </g>
          </g>
          <rect
            class="ehv-line-lime ehv-a-ping"
            x={@cx - 26}
            y={@cy - 26}
            width="52"
            height="52"
            rx="13"
          />
          <g class="ehv-a-pop">
            <rect class="ehv-card-3" x={@cx - 26} y={@cy - 26} width="52" height="52" rx="13" />
            <.mark x={@cx - 20} y={@cy - 20} size="40" />
          </g>
        </g>
      </svg>
    </.frame>
    """
  end

  # Places the names evenly on a ring, the first one at `start` degrees.
  defp ring(names, radius, start, first_index, highlight) do
    count = max(length(names), 1)
    on = highlight && String.upcase(to_string(highlight))

    for {name, index} <- Enum.with_index(names) do
      angle = (start + index * 360 / count) * :math.pi() / 180

      %{
        name: name,
        index: first_index + index,
        on: String.upcase(name) == on,
        glyph: Map.get(@glyphs, String.upcase(name), @fallback_glyph),
        x: Float.round(@cx + radius * :math.cos(angle), 1),
        y: Float.round(@cy + radius * :math.sin(angle), 1)
      }
    end
  end
end
assets/css/integrations/logo-orbit.css
logo-orbit.css
/* integrations/logo-orbit */
.ehv-integrations-logo-orbit { --ehv-max-w: 380px; }

/* A ring turns around the middle of the viewBox. Each seat turns the other
   way around its own centre, so the tile on it stays upright. */
@keyframes ehv-integrations-logo-orbit-cw { to { transform: rotate(360deg); } }
@keyframes ehv-integrations-logo-orbit-ccw { to { transform: rotate(-360deg); } }

.ehv-integrations-logo-orbit__ring { transform-box: view-box; transform-origin: 50% 50%; }
.ehv-integrations-logo-orbit__seat { transform-box: fill-box; transform-origin: center; }

.ehv--animated .ehv-integrations-logo-orbit__ring--inner { animation: ehv-integrations-logo-orbit-cw 56s linear infinite; }
.ehv--animated .ehv-integrations-logo-orbit__ring--inner .ehv-integrations-logo-orbit__seat { animation: ehv-integrations-logo-orbit-ccw 56s linear infinite; }
.ehv--animated .ehv-integrations-logo-orbit__ring--outer { animation: ehv-integrations-logo-orbit-ccw 84s linear infinite; }
.ehv--animated .ehv-integrations-logo-orbit__ring--outer .ehv-integrations-logo-orbit__seat { animation: ehv-integrations-logo-orbit-cw 84s linear infinite; }

.ehv-integrations-logo-orbit__tile--on { stroke: var(--ehv-lime); }

.ehv--hover .ehv-integrations-logo-orbit__tile { transition: stroke 200ms var(--ehv-ease); }
.ehv--hover .ehv-integrations-logo-orbit__tile:hover { stroke: var(--ehv-lime); }
.ehv--hover:hover :is(.ehv-integrations-logo-orbit__ring, .ehv-integrations-logo-orbit__seat) { animation-play-state: paused; }