empowered.house ↗

browser/simple.ex

Browser

A clean browser window with an address bar and a simple page: a heading, a paragraph in skeleton lines and one button.

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

<div class="h-96 rounded-xl border border-border">
  <.browser_simple animated />
</div>
default
isometric
custom address and heading
hover · no glow
light surface
isometric · fade out · still

Props

Prop Type Default What it does
address string "portal.yourcompany.com" Up to 38 characters.
eyebrow string "Customer portal"
heading string "Track every order" Up to 30 characters.
button string "Sign in" Up to 20 characters.
lines integer 3 Skeleton lines of the paragraph, 1 to 5.
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/browser/simple.ex
simple.ex
defmodule EHVisuals.Browser.Simple do
  @moduledoc """
  A clean browser window: an address bar and a simple page with a heading, a
  paragraph in skeleton lines and one button.
  """

  use EHVisuals.Composition,
    name: "browser/simple",
    title: "Browser",
    description:
      "A clean browser window with an address bar and a simple page: a heading, a paragraph in skeleton lines and one button."

  @line_widths [330, 304, 318, 286, 310]

  attr(:address, :string, default: "portal.yourcompany.com", doc: "Up to 38 characters.")
  attr(:eyebrow, :string, default: "Customer portal")
  attr(:heading, :string, default: "Track every order", doc: "Up to 30 characters.")
  attr(:button, :string, default: "Sign in", doc: "Up to 20 characters.")
  attr(:lines, :integer, default: 3, doc: "Skeleton lines of the paragraph, 1 to 5.")

  attr(:label, :string,
    default: "A browser window showing a simple page with a heading, a paragraph and a button"
  )

  frame_attrs()

  @spec browser_simple(map()) :: Phoenix.LiveView.Rendered.t()
  def browser_simple(assigns) do
    count = assigns.lines |> max(1) |> min(5)
    button_y = 148 + count * 12 + 10
    button_w = Float.round(30 + String.length(assigns.button) * 6.2, 1)

    lines =
      for index <- 0..(count - 1) do
        width = if index == count - 1, do: 176, else: Enum.at(@line_widths, index)
        %{index: index, y: 148 + index * 12, w: width}
      end

    assigns =
      assigns
      |> assign(:bones, lines)
      |> assign(:button_y, button_y)
      |> assign(:button_w, button_w)
      |> assign(:height, button_y + 28 + 26)

    ~H"""
    <.frame name="browser/simple" 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">
            <path class="ehv-line-ink ehv-round" d="M21 16 l-4 4 l4 4" />
            <path class="ehv-line ehv-round" d="M33 16 l4 4 l-4 4" />
            <path class="ehv-line-ink ehv-round" d="M56.5 20 a4.5 4.5 0 1 1 -1.6 -3.45" />
            <path class="ehv-line-ink ehv-round" d="M55.3 13.4 v3.3 h-3.3" />
            <rect class="ehv-well" x="68" y="9" width="270" height="22" rx="11" />
            <rect class="ehv-line-ink" x="79" y="18.5" width="7" height="6" rx="1.4" />
            <path class="ehv-line-ink ehv-round" d="M80.5 18.5 v-1.6 a2 2 0 0 1 4 0 v1.6" />
            <text class="ehv-mono ehv-t-2" x="94" y="23" font-size="8.5">{@address}</text>
            <circle class="ehv-node-soft" cx="358" cy="20" r="7" />
            <circle :for={y <- [15, 20, 25]} class="ehv-fill-3" cx="381" cy={y} r="1.25" />
            <line class="ehv-line-soft" x1="0.5" x2="399.5" y1="40.5" y2="40.5" />
          </g>
          <g class="ehv-a-fade ehv-i-2">
            <rect class="ehv-node-soft" x="28.5" y="54.5" width="12" height="12" rx="3.5" />
            <rect class="ehv-fill-strong" x="47" y="58" width="44" height="5" rx="2.5" />
            <rect
              :for={x <- [296, 324, 352]}
              class="ehv-fill-soft"
              x={x}
              y="58.5"
              width="20"
              height="4"
              rx="2"
            />
            <line class="ehv-line-faint" x1="28" x2="372" y1="78.5" y2="78.5" />
          </g>
          <text class="ehv-mono ehv-a-rise ehv-i-3" x="28" y="106" font-size="8.5">
            {String.upcase(@eyebrow)}
          </text>
          <text class="ehv-t-display ehv-a-rise ehv-i-4" x="28" y="133" font-size="21">
            {@heading}
          </text>
          <g :for={line <- @bones} class={"ehv-a-fade ehv-i-#{line.index + 5}"}>
            <rect
              class={"ehv-fill-soft ehv-a-grow-x ehv-i-#{line.index + 5}"}
              x="28"
              y={line.y}
              width={line.w}
              height="6"
              rx="3"
            />
          </g>
          <g class="ehv-a-rise ehv-i-9">
            <g class="ehv-browser-simple__button">
              <rect class="ehv-fill-lime" x="28" y={@button_y} width={@button_w} height="28" rx="7" />
              <text
                class="ehv-t-strong ehv-t-on-lime"
                x={28 + @button_w / 2}
                y={@button_y + 18}
                font-size="11"
                text-anchor="middle"
              >
                {@button}
              </text>
            </g>
            <rect
              class="ehv-line-soft"
              x={28 + @button_w + 10.5}
              y={@button_y + 0.5}
              width="73"
              height="27"
              rx="7"
            />
            <rect
              class="ehv-fill-soft"
              x={28 + @button_w + 28}
              y={@button_y + 11.5}
              width="38"
              height="5"
              rx="2.5"
            />
          </g>
        </g>
      </svg>
    </.frame>
    """
  end
end
assets/css/browser/simple.css
simple.css
/* browser/simple */
.ehv-browser-simple { --ehv-max-w: 440px; }

/* On hover the one button of the page comes forward. */
.ehv-browser-simple__button { transform-box: fill-box; }
.ehv--hover .ehv-browser-simple__button { transition: transform 200ms var(--ehv-ease); }
.ehv--hover:hover .ehv-browser-simple__button { transform: translateY(-2px); }