@@ -20,6 +20,7 @@ import { ArticleMetaApps } from '../components/ArticleMeta.apps';
2020import { ArticleMeta } from '../components/ArticleMeta.web' ;
2121import { ArticleTitle } from '../components/ArticleTitle' ;
2222import { Carousel } from '../components/Carousel.island' ;
23+ import { ContributorAvatar } from '../components/ContributorAvatar' ;
2324import { CricketMatchHeaderWrapper } from '../components/CricketMatchHeaderWrapper.island' ;
2425import { DecideLines } from '../components/DecideLines' ;
2526import { DirectoryPageNavIsland } from '../components/DirectoryPageNavIsland' ;
@@ -52,6 +53,7 @@ import {
5253 type ArticleFormat ,
5354 ArticleSpecial ,
5455} from '../lib/articleFormat' ;
56+ import { getSoleContributor } from '../lib/byline' ;
5557import { canRenderAds } from '../lib/canRenderAds' ;
5658import { getContributionsServiceUrl } from '../lib/contributions' ;
5759import { decideStoryPackageTrails } from '../lib/decideTrail' ;
@@ -81,6 +83,54 @@ const stretchLines = css`
8183 }
8284` ;
8385
86+ const avatarHeadlineWrapper = css `
87+ display : flex;
88+ flex-direction : column;
89+ justify-content : space-between;
90+ ` ;
91+
92+ // This styling taken from the similar approach in CommentLayout.tsx
93+ // If in mobile increase the margin top and margin right deficit
94+ const avatarPositionStyles = css `
95+ display : flex;
96+ justify-content : flex-end;
97+ position : relative;
98+ margin-bottom : -29px ;
99+ pointer-events : none;
100+ ${ from . desktop } {
101+ margin-top : -50px ;
102+ }
103+ ${ until . tablet } {
104+ overflow : hidden;
105+ }
106+
107+ /* Why target img element?
108+
109+ Because only in this context, where we have overflow: hidden
110+ and the margin-bottom and margin-top of avatarPositionStyles
111+ do we also want to apply our margin-right. These styles
112+ are tightly coupled in this context, and so it does not
113+ make sense to move them to the avatar component.
114+
115+ It's imperfect from the perspective of DCR, the alternative is to bust
116+ the combined elements into a separate component (with the
117+ relevant stories) and couple them that way, which might be what
118+ you want to do if you find yourself adding more styles
119+ to this section. For now, this works without making me 🤢.
120+ */
121+
122+ ${ from . mobile } {
123+ img {
124+ margin-right : -1.85rem ;
125+ }
126+ }
127+ ${ from . mobileLandscape } {
128+ img {
129+ margin-right : -1.25rem ;
130+ }
131+ }
132+ ` ;
133+
84134interface GridItemProps {
85135 area : Area ;
86136 layoutType : LayoutType ;
@@ -190,6 +240,13 @@ export const StandardLayout = (props: WebProps | AppProps) => {
190240 ? 'showcase'
191241 : 'standard' ;
192242
243+ const avatarUrl = getSoleContributor (
244+ article . tags ,
245+ article . byline ,
246+ ) ?. bylineLargeImageUrl ;
247+
248+ const displayAvatarUrl = avatarUrl ? true : false ;
249+
193250 return (
194251 < >
195252 { isWeb && (
@@ -285,7 +342,17 @@ export const StandardLayout = (props: WebProps | AppProps) => {
285342 ` ,
286343 ] }
287344 >
288- < GridItem area = "media" layoutType = { layoutType } >
345+ < GridItem
346+ area = "media"
347+ layoutType = { layoutType }
348+ css = {
349+ displayAvatarUrl
350+ ? css `
351+ margin-top : 8px ;
352+ `
353+ : undefined
354+ }
355+ >
289356 < MainMedia
290357 format = { format }
291358 elements = { article . mainMediaElements }
@@ -310,6 +377,11 @@ export const StandardLayout = (props: WebProps | AppProps) => {
310377 area = "title"
311378 layoutType = { layoutType }
312379 element = "aside"
380+ css = { css `
381+ display : flex;
382+ flex-direction : column;
383+ justify-content : space-between;
384+ ` }
313385 >
314386 < ArticleTitle
315387 format = { format }
@@ -319,19 +391,70 @@ export const StandardLayout = (props: WebProps | AppProps) => {
319391 guardianBaseURL = { article . guardianBaseURL }
320392 isMatch = { ! ! footballMatchUrl }
321393 />
394+ { displayAvatarUrl && (
395+ < Hide until = "leftCol" >
396+ < StraightLines
397+ count = { 8 }
398+ cssOverrides = { css `
399+ display : block;
400+ ` }
401+ color = { themePalette ( '--straight-lines' ) }
402+ />
403+ </ Hide >
404+ ) }
322405 </ GridItem >
323- < GridItem area = "headline" layoutType = { layoutType } >
324- < ArticleHeadline
325- format = { format }
326- headlineString = { article . headline }
327- tags = { article . tags }
328- byline = { article . byline }
329- webPublicationDateDeprecated = {
330- article . webPublicationDateDeprecated
331- }
332- starRating = { article . starRating }
333- />
334- </ GridItem >
406+ { displayAvatarUrl ? (
407+ < GridItem area = "headline" layoutType = { layoutType } >
408+ < div css = { avatarHeadlineWrapper } >
409+ < ArticleHeadline
410+ format = { format }
411+ headlineString = { article . headline }
412+ tags = { article . tags }
413+ byline = { article . byline }
414+ webPublicationDateDeprecated = {
415+ article . webPublicationDateDeprecated
416+ }
417+ hasAvatar = { displayAvatarUrl }
418+ starRating = { article . starRating }
419+ />
420+
421+ < div >
422+ { ! ! avatarUrl && (
423+ < div css = { avatarPositionStyles } >
424+ < ContributorAvatar
425+ imageSrc = { avatarUrl }
426+ imageAlt = {
427+ article . byline ?? ''
428+ }
429+ />
430+ </ div >
431+ ) }
432+ < StraightLines
433+ count = { 8 }
434+ cssOverrides = { css `
435+ display : block;
436+ ` }
437+ color = { themePalette (
438+ '--straight-lines' ,
439+ ) }
440+ />
441+ </ div >
442+ </ div >
443+ </ GridItem >
444+ ) : (
445+ < GridItem area = "headline" layoutType = { layoutType } >
446+ < ArticleHeadline
447+ format = { format }
448+ headlineString = { article . headline }
449+ tags = { article . tags }
450+ byline = { article . byline }
451+ webPublicationDateDeprecated = {
452+ article . webPublicationDateDeprecated
453+ }
454+ starRating = { article . starRating }
455+ />
456+ </ GridItem >
457+ ) }
335458 < GridItem area = "standfirst" layoutType = { layoutType } >
336459 < Standfirst
337460 format = { format }
@@ -348,12 +471,12 @@ export const StandardLayout = (props: WebProps | AppProps) => {
348471 format . theme === ArticleSpecial . Labs &&
349472 format . design !== ArticleDesign . Video ? (
350473 < GuardianLabsLines />
351- ) : (
474+ ) : ! displayAvatarUrl ? (
352475 < DecideLines
353476 format = { format }
354477 color = { themePalette ( '--article-border' ) }
355478 />
356- ) }
479+ ) : null }
357480 </ div >
358481 { isApps ? (
359482 < >
0 commit comments