integrations/logo-orbit.ex
Logo orbit
System tiles orbiting the EmpoweredHouse mark on two rings.
# in the module that renders the template
use EHVisuals
<div class="h-96 rounded-xl border border-border">
<.integrations_logo_orbit animated />
</div>
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
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
/* 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; }