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.
# in the module that renders the template
use EHVisuals
<div class="h-96 rounded-xl border border-border">
<.browser_simple animated />
</div>
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
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
/* 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); }