empowered.house ↗

brand/spotlight.ex

Spotlight

The EmpoweredHouse app icon lit by the warm glow above a fading row of plain app tiles.

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

<div class="h-96 rounded-xl border border-border">
  <.brand_spotlight animated />
</div>
default
isometric
two rows · custom copy
five tiles · no tagline · hover
light surface
two rows · fade out · no glow · still

Props

Prop Type Default What it does
name string "EmpoweredHouse" The line under the icon.
tagline any "Software shaped to your company" The mono line under the name. `nil` hides it.
tiles integer 7 Placeholder tiles per row, three to seven.
rows integer 1 One or two rows of placeholder tiles.
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/brand/spotlight.ex
spotlight.ex
defmodule EHVisuals.Brand.Spotlight do
  @moduledoc """
  The EmpoweredHouse app icon in the warm light, above a row of plain app
  tiles that fades towards its ends.
  """

  use EHVisuals.Composition,
    name: "brand/spotlight",
    title: "Spotlight",
    description:
      "The EmpoweredHouse app icon lit by the warm glow above a fading row of plain app tiles."

  attr(:name, :string, default: "EmpoweredHouse", doc: "The line under the icon.")

  attr(:tagline, :any,
    default: "Software shaped to your company",
    doc: "The mono line under the name. `nil` hides it."
  )

  attr(:tiles, :integer, default: 7, doc: "Placeholder tiles per row, three to seven.")
  attr(:rows, :integer, default: 1, doc: "One or two rows of placeholder tiles.")

  attr(:label, :string,
    default: "The EmpoweredHouse app icon lit from above, over a row of plain app tiles"
  )

  frame_attrs()

  @spec brand_spotlight(map()) :: Phoenix.LiveView.Rendered.t()
  def brand_spotlight(assigns) do
    count = assigns.tiles |> max(3) |> min(7)
    rows = assigns.rows |> max(1) |> min(2)
    left = 200 - (count * 52 - 12) / 2
    top = if assigns.tagline, do: 214, else: 198

    tiles =
      for row <- 0..(rows - 1), index <- 0..(count - 1) do
        distance = round(abs(index - (count - 1) / 2)) + row

        %{
          x: Float.round(left + index * 52, 1),
          y: top + row * 52,
          distance: min(distance, 4),
          stagger: min(distance + 5, 24)
        }
      end

    assigns =
      assigns
      |> assign(:items, tiles)
      |> assign(:height, top + rows * 52 + 8)

    ~H"""
    <.frame name="brand/spotlight" label={@label} {frame_opts(assigns)}>
      <svg viewBox={"0 0 400 #{@height}"}>
        <rect
          class="ehv-line-faint ehv-a-fade ehv-i-3"
          x="128.5"
          y="10.5"
          width="143"
          height="143"
          rx="40"
        />
        <rect
          class="ehv-line-soft ehv-a-fade ehv-i-2"
          x="140.5"
          y="22.5"
          width="119"
          height="119"
          rx="32"
        />
        <g class="ehv-a-pop ehv-i-0">
          <g class="ehv-h-lift">
            <rect class="ehv-brand-spotlight__icon" x="152.5" y="34.5" width="95" height="95" rx="24" />
            <.mark x="156" y="38" size="88" />
          </g>
        </g>
        <g class="ehv-a-fade ehv-i-4">
          <text class="ehv-t-strong" x="200" y="180" font-size="14" text-anchor="middle">
            {@name}
          </text>
          <text :if={@tagline} class="ehv-mono" x="200" y="197" font-size="8.5" text-anchor="middle">
            {String.upcase(@tagline)}
          </text>
        </g>
        <g :for={tile <- @items} class={"ehv-a-pop ehv-i-#{tile.stagger}"}>
          <rect
            class={"ehv-brand-spotlight__tile ehv-brand-spotlight__tile--#{tile.distance}"}
            x={tile.x + 0.5}
            y={tile.y + 0.5}
            width="39"
            height="39"
            rx="11"
          />
        </g>
      </svg>
    </.frame>
    """
  end
end
assets/css/brand/spotlight.css
spotlight.css
/* brand/spotlight */
.ehv-brand-spotlight { --ehv-max-w: 440px; }

/* The glow sits over the icon, not in the middle of the frame. */
.ehv-brand-spotlight .ehv__glow {
  background: radial-gradient(40% 46% at 50% 34%, var(--ehv-glow), transparent 72%);
  opacity: 0.8;
}

.ehv-brand-spotlight__icon { fill: var(--ehv-surface-3); stroke: var(--ehv-border-strong); stroke-width: 1; }

/* Plain tiles, fainter the further they are from the icon. */
.ehv-brand-spotlight__tile { fill: var(--ehv-surface-3); stroke: var(--ehv-ink-4); stroke-width: 1; }
.ehv-brand-spotlight__tile--0 { opacity: 1; }
.ehv-brand-spotlight__tile--1 { opacity: 0.78; }
.ehv-brand-spotlight__tile--2 { opacity: 0.52; }
.ehv-brand-spotlight__tile--3 { opacity: 0.3; }
.ehv-brand-spotlight__tile--4 { opacity: 0.15; }

/* The outline of the mark draws itself, then the lime core lands. */
.ehv--animated.ehv-brand-spotlight .ehv-mark__frame {
  stroke-dasharray: 1 1;
  animation: ehv-draw 1.1s var(--ehv-ease) 300ms backwards;
}
.ehv-brand-spotlight .ehv-mark__core { transform-box: fill-box; transform-origin: center; }
.ehv--animated.ehv-brand-spotlight .ehv-mark__core { animation: ehv-pop 500ms var(--ehv-ease) 1000ms backwards; }

.ehv--hover .ehv-brand-spotlight__tile { transition: opacity 200ms var(--ehv-ease), stroke 200ms var(--ehv-ease); }
.ehv--hover .ehv-brand-spotlight__tile:hover { opacity: 1; stroke: var(--ehv-ink-2); }