Overview
The contact-box is a responsive, flexible, and customizable UI component used to display contact information such as name, image, phone, email, additional details, and an optional action button. It's designed to adapt across different screen sizes and supports a variety of layout options for positioning text and images.
Capabilities
-
Display a contact person's name, image, phone, email, and a detail text
-
Responsive layout using custom media queries
-
Supports multiple image positions relative to the text (
left,right,top,bottom) -
Supports different image sizes (
small,big) -
Custom alignment of contact information relative to name (
under,parallel,reverse-parallel) -
Toggleable detail position relative to name (
top,bottom) -
Optional action button with a link and label
Configuration Options
These options can be changed by setting template variables before the component block.
| Variable Name | Type | Default Value | Possible Values | Description |
|---|---|---|---|---|
$imagePosition |
string | "left" |
"left", "right", "top", "bottom" |
Defines where the image appears relative to the text. |
$imageSize |
string | "small" |
"small", "big" |
Sets the size of the contact image. |
$contactInfoPosition |
string | "under" |
"under", "parallel", "reverse-parallel" |
Controls where the contact info (phone/email) appears relative to the name. |
$detailPosition |
string | "top" |
"top", "bottom" |
Decides if the detail text is above or below the name. |
$button |
string | null |
"Label::link" format or null |
Adds a button below or beside the contact info. |
ℹ️ Note: The $contact variable (array) is required and must contain the contact data. It does not have a default value and must be passed explicitly.
Examples
With default values + button
{include ./contact-box.latte, contact=>$contact, button=>"Nezávazná poptávka::#"}

With modified values
{include ./contact-box.latte, contact=>$contact, imageSize=>"big", contactInfoPosition=>"parallel", button=>"Nezávazná poptávka::#"}

Code
Latte
{* contact data *}
{default $contact = []}
{* image position relative to text *}
{* possible options: top, left, bottom, right *}
{default $imagePosition = "left"}
{* image size *}
{* possible options: small, big *}
{default $imageSize = "small"}
{* contact info position relative to name wrap *}
{* possible options: under, parallel, reverse-parallel *}
{default $contactInfoPosition = "under"}
{* detail position relative to name *}
{* possible options: bottom, top *}
{default $detailPosition = 'top'}
{* button in format Title::link *}
{default $button = null}
<div class="contact-box contact-box--image-{$imageSize} contact-box--image-{$imagePosition} --flex --w-3" n:if="!empty($contact)">
{var $img = ("/menuitem/small/img-{$contact['id']}-4.jpg"|imageExist)}
<div class="contact-box__figure contact-box__figure--big" n:if="$img && $imageSize === 'big'">
<img src="{$img}" alt="{$contact|name}" class="contact-box__img">
</div>
<div class="contact-box__texts contact-box__texts--{$contactInfoPosition}">
<div class="contact-box__top contact-box__top--{$contactInfoPosition}">
<div class="contact-box__top-wrap contact-box--image-{$imagePosition}">
<div class="contact-box__figure contact-box__figure--{$imagePosition} contact-box__figure--{$imageSize}" n:if="$img && $imageSize === 'small'">
<img src="{$img}" alt="{$contact|name}" class="contact-box__img">
</div>
<div class="contact-box__name contact-box__name--{$detailPosition}">
{$contact|name|noescape}
<p class="contact-box__detail">{$contact['supplements']['Detail']}</p>
</div>
</div>
<div class="contact-box__contacts" n:if="$contactInfoPosition !== 'under'">
<a href="tel:{$web['PER_PHONE']}{$contact['supplements']['Telefon']|noescape}"
class="contact-box__contact contact-box__phone"
n:if="$contact['supplements']['Telefon']">
{$web['PER_PHONE']} {$contact['supplements']['Telefon']|noescape}
</a>
<a href="mailto:{$contact['supplements']['Email']|noescape}"
class="contact-box__contact contact-box__mail"
n:if="$contact['supplements']['Email']">
{$contact['supplements']['Email']|noescape}
</a>
</div>
</div>
<div class="contact-box__bottom">
<div class="contact-box__contacts" n:if="$contactInfoPosition === 'under'">
<a href="tel:{$web['PER_PHONE']}{$contact['supplements']['Telefon']|noescape}"
class="contact-box__contact contact-box__phone"
n:if="$contact['supplements']['Telefon']">
{$web['PER_PHONE']} {$contact['supplements']['Telefon']|noescape}
</a>
<a href="mailto:{$contact['supplements']['Email']|noescape}"
class="contact-box__contact contact-box__mail"
n:if="$contact['supplements']['Email']">
{$contact['supplements']['Email']|noescape}
</a>
</div>
{if !empty($button) && $contactInfoPosition === 'under'}
{var $button = explode('::', $button)}
<a class="button button--brand contact-box__button" n:if="$button" href="{$button[1]}">
{$button[0]}
</a>
{/if}
</div>
{if !empty($button) && $contactInfoPosition !== 'under'}
{var $button = explode('::', $button)}
<a class="button button--brand contact-box__button contact-box__button-parallel" n:if="$button" href="{$button[1]}">
{$button[0]}
</a>
{/if}
</div>
</div>
SCSS
.contact-box {
margin-left: 10rem;
padding: 17px 15px 23px 15px;
background: $color__bg;
box-shadow: $shadow__main;
display: flex;
}
.contact-box__top {
display: flex;
flex-direction: row;
gap: 15px;
align-items: center;
margin-bottom: 16px;
@include media($large) {
flex-direction: column;
align-items: start;
}
@include media($xlarge) {
flex-direction: row;
}
}
.contact-box__wrap {
align-items: flex-start;
}
.contact-box__title {
font-size: 20px;
color: $color__brand;
margin-bottom: 11px;
}
.contact-box__detail {
font-weight: normal;
font-size: 16px;
width: fit-content;
text-wrap: wrap;
@include media($medium) {
text-wrap: nowrap;
}
}
.contact-box__name {
font-weight: 700;
font-size: 17px;
align-self: center;
display: flex;
}
.contact-box__contact-wrap {
display: flex;
flex-direction: column;
align-items: center;
@include media($tiny) {
flex-direction: row;
align-items: center;
}
@include media($medium) {
flex-direction: column;
}
@include media($large) {
}
@include media($xlarge) {
flex-direction: row;
align-items: start;
}
}
.contact-box__texts {
display: flex;
align-items: center;
@include media($tiny) {
align-items: flex-start;
}
@include media($medium) {
align-items: center;
}
@include media($large) {
align-items: start;
}
@include media($xlarge) {
align-items: flex-start;
}
}
.contact-box__contacts {
display: flex;
flex-direction: column;
gap: 3px;
align-items: flex-start;
@include media($tiny) {
align-items: flex-start;
}
@include media($large) {
align-items: flex-start;
}
@include media($xlarge) {
font-size: 17px;
}
}
.contact-box__contact {
font-weight: 500;
color: $color__text;
font-size: 14px;
@include media($xlarge) {
font-size: 17px;
}
}
.contact-box__figure {
width: 57px;
height: 57px;
display: flex;
align-items: center;
justify-content: center;
}
.contact-box__img {
width: 100%;
height: 100%;
object-fit: cover;
}
.contact-box__mail {
text-decoration: underline;
}
.contact-box__button {
margin-top: 15px;
}
.contact-box__top-wrap {
display: flex;
flex-direction: row;
}
// modifications
.contact-box--image {
&-left {
flex-direction: row;
}
&-right {
flex-direction: row-reverse;
}
&-top {
flex-direction: column;
}
&-bottom {
flex-direction: column-reverse;
}
&-big {
flex-wrap: nowrap;
gap: 30px;
}
}
.contact-box__figure- {
&-right {
margin-left: 10px;
}
&-left {
margin-right: 10px;
}
}
.contact-box__texts- {
&-under {
flex-direction: column;
}
&-parallel {
flex-direction: column;
}
&-reverse-parallel {
flex-direction: column;
}
}
.contact-box__figure- {
&-big {
width: 150px;
height: 150px;
align-self: center;
}
&-small {
//margin-right: 10px;
}
}
.contact-box__button-parallel {
}
.contact-box__name- {
&-bottom {
flex-direction: column;
}
&-top {
flex-direction: column-reverse;
}
}
.contact-box__top- {
&-under {
}
&-parallel {
flex-direction: row;
position: relative;
justify-content: space-between;
align-items: center;
margin-bottom: 0;
}
&-reverse-parallel {
flex-direction: row-reverse;
position: relative;
justify-content: space-between;
align-items: center;
margin-bottom: 0;
}
}