File: //var/www/quadcode.com/src/components/blocks/hero/Hero.svelte
<script lang="ts">
import type { IPost } from "$type/post";
import {locale, localeLink, t} from '$lib/translations';
import expert from "../../../assets/post/expert-icon-big.svg";
export let data: IPost;
const modifiedDate = new Date(data.modified);
const createdDate = new Date(data.date);
const modifiedMonthName = modifiedDate.toLocaleString($locale, { month: 'long' });
const createdMonthName = createdDate.toLocaleString($locale, { month: 'long' });
const modifiedText = modifiedMonthName + ' ' + modifiedDate.getDate() + ', ' + modifiedDate.getFullYear();
const createdText = createdMonthName + ' ' + createdDate.getDate() + ', ' + createdDate.getFullYear();
</script>
<div class="hero">
<div>
<div class="hero__content">
<div class="article-hero-badges">
{#if data?.expertData}
<div class="expert-badge-wrap">
<button type="button" class="expert-badge-btn" id="expertBadgeBtn" aria-expanded="false" aria-haspopup="true" aria-label="Expert reviewed – click for details">
<img src={expert} alt="" width="18" height="18">
{$t('Expert reviewed')}
</button>
<div class="expert-badge-tooltip" id="expertBadgeTooltip" role="tooltip">
{$t('Our subject matter experts have reviewed this article to ensure it meets the highest standard for accurate information and guidance. Learn more about our <a href="/blog/editorial-standards">editorial standards</a> and <a href="/blog/editorial-process">process</a>.')}
</div>
</div>
{/if}
{#if data?.tagsData}
{#each data.tagsData as tag}
<div class="article-tag">{tag}</div>
{/each}
{/if}
</div>
<h1 class="hero__title">{@html data.title.rendered}</h1>
<div class="article-hero-meta">
<div class="article-meta">
<div class="article-meta-author">
<div class="article-meta-avatar">
{#if data?.authorData?.avatar?.thumbnail}
<img
src={data.authorData.avatar.thumbnail}
alt={data.authorData.name}
class="hero__avatar"
/>
{/if}
</div>
<div>
<span class="article-meta-name-row">
<a href="{localeLink()}/author/{data.authorData.slug}" class="article-meta-name">{data.authorData.name}</a>
{#if data.authorData.linkedin}
<a
href={data.authorData.linkedin}
target="_blank"
rel="noopener noreferrer"
class="article-meta-linkedin"
aria-label="LinkedIn"
>
<svg viewBox="0 0 24 24" fill="#0a66c2" width="14" height="14"><path d="M20.447 20.452h-3.554v-5.569c0-1.328-.027-3.037-1.852-3.037-1.853 0-2.136 1.445-2.136 2.939v5.667H9.351V9h3.414v1.561h.046c.477-.9 1.637-1.85 3.37-1.85 3.601 0 4.267 2.37 4.267 5.455v6.286zM5.337 7.433c-1.144 0-2.063-.926-2.063-2.065 0-1.138.92-2.063 2.063-2.063 1.14 0 2.064.925 2.064 2.063 0 1.139-.925 2.065-2.064 2.065zm1.782 13.019H3.555V9h3.564v11.452zM22.225 0H1.771C.792 0 0 .774 0 1.729v20.542C0 23.227.792 24 1.771 24h20.451C23.2 24 24 23.227 24 22.271V1.729C24 .774 23.2 0 22.222 0h.003z"></path></svg>
</a>
{/if}
</span>
<div class="article-meta-detail">{data.authorData.position}</div>
</div>
</div>
<span class="article-meta-sep"></span>
<span class="article-meta-detail">{data.estReadingTime} {$t('blog.readingTimeSuffix')}</span>
<span class="article-meta-sep"></span>
<span class="article-meta-detail">{createdText}</span>
<span class="article-meta-sep"></span>
<span class="article-meta-detail">{$t('Updated')} {modifiedText}</span>
<span class="article-meta-sep"></span>
<span class="article-meta-detail">{data.views} {$t('blog.viewsLabel')}</span>
</div>
<p class="article-contributors"><strong>{$t('blog.contributorsLabel')}</strong>
{#if data.authorData}
<a href="{localeLink()}/author/{data.authorData.slug}">{data.authorData.name}</a>
{/if}
{#if data.expertData}
, <a href="{localeLink()}/author/{data.expertData.slug}">{data.expertData.name}</a>
{/if}
</div>
</div>
</div>
</div>
<style lang="scss">
@import 'src/scss/media';
@import 'src/scss/mixins';
@import 'src/scss/variables';
.article-hero-badges { display: flex; align-items: center; gap: 12px; flex-wrap: wrap; margin-bottom: 16px; }
.article-badge {
font-size: 0.75rem; font-weight: 600; padding: 4px 10px; border-radius: 4px;
background: #f0f9ff; color: #0369a1; border: 1px solid #bae6fd;
}
.expert-badge-wrap { position: relative; display: inline-block; }
.expert-badge-btn {
display: inline-flex; align-items: center; gap: 6px; padding: 6px 12px;
color: #374151;
background: #fff; border: 1px solid #e5e7eb; border-radius: 6px;
cursor: pointer; transition: border-color 0.2s, box-shadow 0.2s;
line-height: 1;
font-size: 0.75rem;
font-weight: 600;
}
.expert-badge-btn:hover { border-color: #d1d5db; box-shadow: 0 1px 3px rgba(0,0,0,0.06); }
.expert-badge-btn img { width: 18px; height: 18px; display: block; flex-shrink: 0; }
.expert-badge-tooltip {
display: none; position: absolute; left: 0; top: calc(100% + 8px); z-index: 200;
width: 320px; max-width: 90vw; padding: 14px 16px;
background: #fff; border: 1px solid #e5e7eb; border-radius: 8px;
box-shadow: 0 10px 25px rgba(0,0,0,0.12); font-size: 0.8125rem; line-height: 1.5; color: #374151;
}
.expert-badge-tooltip::before {
content: ''; position: absolute; left: 16px; top: -6px;
border-left: 6px solid transparent; border-right: 6px solid transparent;
border-bottom: 6px solid #e5e7eb;
}
.expert-badge-tooltip::after {
content: ''; position: absolute; left: 17px; top: -5px;
border-left: 5px solid transparent; border-right: 5px solid transparent;
border-bottom: 5px solid #fff;
}
.expert-badge-tooltip.is-open { display: block; }
.expert-badge-tooltip a { color: #E62334; text-decoration: underline; text-underline-offset: 2px; }
.expert-badge-tooltip a:hover { color: #c41e2a; }
.article-hero-meta { padding-bottom: 32px; border-bottom: 1px solid #eaedf0; }
.article-meta {
display: flex; align-items: center; gap: 16px; flex-wrap: wrap;
}
.article-tag {
font-family: 'Inter', system-ui, -apple-system, sans-serif;
font-weight: 600;
display: inline-block;
font-size: 0.75rem;
line-height: 1.7;
color: #E62334;
background: rgba(230, 35, 52, 0.08);
padding: 4px 12px;
border-radius: 4px;
}
.article-contributors {
font-size: 0.8125rem; color: #5a6872; margin-top: 12px;
}
.article-contributors strong { color: #374151; font-weight: 600; }
.article-contributors a { color: #E62334; text-decoration: underline; text-underline-offset: 2px; }
.article-contributors a:hover { color: #c41e2a; }
.article-meta-author {
display: flex; align-items: center; gap: 12px;
}
.article-meta-avatar {
width: 44px; height: 44px; overflow: hidden; flex-shrink: 0;
clip-path: url(#authorShape);
-webkit-clip-path: url(#authorShape);
border: 1px solid #e5e7eb;
}
.article-meta-avatar img { width: 100%; height: 100%; object-fit: cover; }
.article-meta-name-row {
display: inline-flex; align-items: center; gap: 5px; flex-wrap: wrap;
}
.article-meta-name {
font-size: 0.875rem; font-weight: 600; color: #141414;
text-decoration: none;
}
.article-meta-name:hover { text-decoration: underline; }
.article-meta-linkedin {
display: inline-flex; line-height: 0; color: #0a66c2;
transition: opacity 0.2s;
}
.article-meta-linkedin:hover { opacity: 0.8; }
.article-meta-detail { font-size: 0.8125rem; color: #889aa8; }
.article-meta-sep { width: 4px; height: 4px; border-radius: 50%; background: #c5cfd6; }
.hero {
width: 100%;
padding: 32px 0;
@media (max-width: 768px) {
padding-top: 16px; padding-bottom: 10px;
}
&__title {
font-size: clamp(1.75rem, 3.2vw, 2.5rem); font-weight: 800;
line-height: 1.2; color: #141414; letter-spacing: -0.02em;
margin-bottom: 20px; max-width: 780px;
font-family: 'Inter', system-ui, -apple-system, sans-serif;
}
&__meta {
display: flex;
align-items: center;
gap: 12px;
}
&__avatar {
width: 40px;
height: 40px;
border-radius: 50%;
object-fit: cover;
flex-shrink: 0;
}
&__metaText {
display: flex;
flex-direction: column;
gap: 2px;
}
&__authorName {
font-family: Suisse Intl;
font-weight: 600;
font-size: 14px;
line-height: 20px;
color: $fontPrimary;
}
&__metaDetails {
display: flex;
align-items: center;
gap: 6px;
flex-wrap: wrap;
font-family: Suisse Intl;
font-weight: 400;
font-size: 13px;
line-height: 18px;
color: $techBlueSecondary;
}
&__sep {
color: $techBlue3;
user-select: none;
}
@media (max-width: 768px) {
.qc-topbar { display: none; }
.qc-header-nav { display: none; }
.qc-header-inner, .article-wrapper { padding-left: 20px; padding-right: 20px; max-width: 100%; }
.article-hero .breadcrumbs, .article-hero .article-header { padding-left: 20px; padding-right: 20px; }
.article-hero .article-header { padding-top: 16px; padding-bottom: 10px; }
.article-hero-meta { padding-bottom: 10px; }
.article-body { padding-top: 12px; }
.qc-footer-grid { grid-template-columns: repeat(2, 1fr); }
.sidebar { flex-direction: column; }
.author-card { flex-direction: column; align-items: center; text-align: center; }
.qc-footer-inner { padding: 0 20px; }
/* Hide author detail at top of article on mobile */
.article-meta-author, .article-contributors { display: none !important; }
.sidebar { display: none !important; }
}
}
</style>