Skip to content

Commit 8af6357

Browse files
authored
Merge pull request #84 from adobecom/stage
Release [Stage to Main]
2 parents 5b80e69 + 9bed355 commit 8af6357

4 files changed

Lines changed: 486 additions & 0 deletions

File tree

Lines changed: 145 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,145 @@
1+
.blog-author > div:not(.blog-author-content) {
2+
display: contents;
3+
}
4+
5+
.blog-author {
6+
display: flex;
7+
flex-direction: column;
8+
padding: 56px 30px;
9+
}
10+
11+
.blog-author-image {
12+
margin-bottom: 24px;
13+
}
14+
15+
.blog-author-image img {
16+
display: block;
17+
width: 100%;
18+
max-width: 148px;
19+
aspect-ratio: 1;
20+
object-fit: cover;
21+
object-position: top center;
22+
border-radius: 50%;
23+
}
24+
25+
.blog-author-name {
26+
font-size: 36px;
27+
font-weight: 700;
28+
line-height: 1.2;
29+
margin: 0 0 8px;
30+
}
31+
32+
.blog-author-title {
33+
font-size: 20px;
34+
font-weight: 700;
35+
margin: 0 0 8px;
36+
}
37+
38+
.blog-author-description {
39+
font-size: 20px;
40+
margin: 0;
41+
}
42+
43+
.blog-author-social {
44+
display: flex;
45+
flex-wrap: wrap;
46+
gap: 8px;
47+
align-items: center;
48+
margin-top: 32px;
49+
}
50+
51+
.blog-author-social a {
52+
display: flex;
53+
align-items: center;
54+
justify-content: center;
55+
width: 28px;
56+
height: 28px;
57+
background: var(--color-gray-700, #2c2c2c);
58+
border-radius: 8px;
59+
color: #fff;
60+
text-decoration: none;
61+
}
62+
63+
.blog-author-social .icon {
64+
display: contents;
65+
}
66+
67+
.blog-author-social .icon svg {
68+
display: block;
69+
width: 16px;
70+
height: 16px;
71+
transform: translateY(-2px);
72+
}
73+
74+
@media (min-width: 600px) {
75+
.blog-author {
76+
padding: 56px 32px;
77+
}
78+
79+
.blog-author-image picture {
80+
width: 300px;
81+
}
82+
83+
.blog-author-image img {
84+
width: 300px;
85+
max-width: none;
86+
aspect-ratio: auto;
87+
}
88+
89+
.blog-author-social {
90+
gap: 16px;
91+
}
92+
}
93+
94+
@media (min-width: 768px) {
95+
.blog-author {
96+
padding: 56px 48px;
97+
}
98+
99+
.blog-author-image {
100+
margin-bottom: 52px;
101+
}
102+
103+
.blog-author-image picture {
104+
width: 328px;
105+
}
106+
107+
.blog-author-image img {
108+
width: 328px;
109+
max-width: none;
110+
aspect-ratio: auto;
111+
}
112+
113+
}
114+
115+
@media (min-width: 1200px) {
116+
.blog-author {
117+
display: grid;
118+
grid-template-columns: 328px minmax(0, 600px);
119+
column-gap: 32px;
120+
align-items: center;
121+
justify-content: center;
122+
}
123+
124+
.blog-author-image {
125+
grid-column: 1;
126+
margin-bottom: 0;
127+
}
128+
129+
.blog-author-image picture {
130+
display: contents;
131+
}
132+
133+
.blog-author-image img {
134+
width: 328px;
135+
max-width: none;
136+
height: 328px;
137+
aspect-ratio: 1;
138+
object-fit: cover;
139+
object-position: top center;
140+
}
141+
142+
.blog-author-content {
143+
grid-column: 2;
144+
}
145+
}
Lines changed: 131 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,131 @@
1+
import { LIBS } from '../../scripts/scripts.js';
2+
3+
const iconsModule = await import(`${LIBS}/features/icons/icons.js`).catch(() => null);
4+
const loadIcons = iconsModule?.default ?? (() => {});
5+
6+
const SOCIAL_PLATFORMS = {
7+
'linkedin.com': { name: 'LinkedIn', icon: 'linkedin' },
8+
'twitter.com': { name: 'X', icon: 'twitter' },
9+
'x.com': { name: 'X', icon: 'twitter' },
10+
'facebook.com': { name: 'Facebook', icon: 'facebook' },
11+
'instagram.com': { name: 'Instagram', icon: 'instagram' },
12+
};
13+
14+
function resolvePlatform(href) {
15+
return Object.keys(SOCIAL_PLATFORMS).find((domain) => href?.includes(domain));
16+
}
17+
18+
function decorateSocial(row) {
19+
const links = [...row.querySelectorAll('a')];
20+
row.replaceChildren(...links);
21+
row.className = 'blog-author-social';
22+
links.forEach((a) => {
23+
const domain = resolvePlatform(a.href);
24+
if (!domain) { a.hidden = true; return; }
25+
const { name, icon } = SOCIAL_PLATFORMS[domain];
26+
a.setAttribute('aria-label', name);
27+
a.target = '_blank';
28+
a.rel = 'noopener noreferrer';
29+
const span = document.createElement('span');
30+
span.className = `icon icon-${icon}`;
31+
a.replaceChildren(span);
32+
});
33+
loadIcons(row.querySelectorAll('span.icon'));
34+
}
35+
36+
function injectSchema(el, company) {
37+
const name = el.querySelector('.blog-author-name')?.textContent;
38+
if (!name) return;
39+
40+
const schema = {
41+
'@context': 'https://schema.org',
42+
'@type': 'Person',
43+
name,
44+
url: window.location.href,
45+
};
46+
47+
if (company) {
48+
schema.worksFor = { '@type': 'Organization', name: company };
49+
}
50+
51+
const title = el.querySelector('.blog-author-title')?.textContent;
52+
if (title) schema.jobTitle = title;
53+
54+
const desc = [...el.querySelectorAll('.blog-author-description')]
55+
.map((p) => p.textContent).join(' ');
56+
if (desc) schema.description = desc;
57+
58+
const img = el.querySelector('picture img')?.src;
59+
if (img) schema.image = img;
60+
61+
const sameAs = [...el.querySelectorAll('.blog-author-social a:not([hidden])')]
62+
.map((a) => a.getAttribute('aria-label') && a.href).filter(Boolean);
63+
if (sameAs.length) schema.sameAs = sameAs;
64+
65+
const script = document.createElement('script');
66+
script.type = 'application/ld+json';
67+
script.textContent = JSON.stringify(schema);
68+
document.head.append(script);
69+
}
70+
71+
export default async function init(el) {
72+
let socialContainer = null;
73+
let textIdx = 0;
74+
let company = null;
75+
const HEX = '#[0-9a-fA-F]{3,6}';
76+
const TEXT_CLASSES = ['blog-author-name', 'blog-author-title', 'blog-author-description'];
77+
78+
function decorateRow(row) {
79+
const text = row.textContent.trim();
80+
81+
const gradient = text.match(new RegExp(`^(${HEX})\\s*,\\s*(${HEX})$`));
82+
if (gradient) {
83+
el.style.background = `linear-gradient(to bottom, ${gradient[1]}, ${gradient[2]})`;
84+
row.parentElement.remove();
85+
return;
86+
}
87+
88+
if (new RegExp(`^${HEX}$`).test(text)) {
89+
el.style.backgroundColor = text;
90+
row.parentElement.remove();
91+
return;
92+
}
93+
94+
if (row.querySelector('picture')) {
95+
row.className = 'blog-author-image';
96+
return;
97+
}
98+
99+
if ([...row.querySelectorAll('a')].some((a) => resolvePlatform(a.href))) {
100+
if (!socialContainer) {
101+
socialContainer = row;
102+
} else {
103+
[...row.querySelectorAll('a')].forEach((a) => socialContainer.append(a));
104+
row.parentElement.remove();
105+
}
106+
return;
107+
}
108+
109+
if (socialContainer) {
110+
company = text;
111+
row.parentElement.remove();
112+
return;
113+
}
114+
115+
row.className = TEXT_CLASSES[Math.min(textIdx, 2)];
116+
textIdx += 1;
117+
}
118+
119+
el.querySelectorAll(':scope > div > div').forEach(decorateRow);
120+
121+
if (socialContainer) decorateSocial(socialContainer);
122+
123+
const content = document.createElement('div');
124+
content.className = 'blog-author-content';
125+
el.querySelectorAll('.blog-author-name, .blog-author-title, .blog-author-description, .blog-author-social')
126+
.forEach((t) => content.append(t));
127+
el.querySelectorAll(':scope > div:not(:has(*))').forEach((d) => d.remove());
128+
el.append(content);
129+
130+
injectSchema(el, company);
131+
}

blog/scripts/scripts.js

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -103,6 +103,7 @@ const STYLES = '/blog/styles/styles.css';
103103
// Add any config options.
104104
const CONFIG = {
105105
imsClientId: 'bacom',
106+
imsScope: 'AdobeID,openid,gnav,pps.read,firefly_api,additional_info.roles,read_organizations,account_cluster.read',
106107
stage: { edgeConfigId: '7d1ba912-10b6-4384-a8ff-4bfb1178e869' },
107108
prod: { edgeConfigId: '65acfd54-d9fe-405c-ba04-8342d6782ab0' },
108109
locales: {

0 commit comments

Comments
 (0)