@@ -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 ;
@@ -182,6 +232,13 @@ export const StandardLayout = (props: WebProps | AppProps) => {
182232 ? 'showcase'
183233 : 'standard' ;
184234
235+ const avatarUrl = getSoleContributor (
236+ article . tags ,
237+ article . byline ,
238+ ) ?. bylineLargeImageUrl ;
239+
240+ const displayAvatarUrl = avatarUrl ? true : false ;
241+
185242 return (
186243 < >
187244 { isWeb && (
@@ -276,7 +333,17 @@ export const StandardLayout = (props: WebProps | AppProps) => {
276333 ` ,
277334 ] }
278335 >
279- < GridItem area = "media" layoutType = { layoutType } >
336+ < GridItem
337+ area = "media"
338+ layoutType = { layoutType }
339+ css = {
340+ displayAvatarUrl
341+ ? css `
342+ margin-top : 8px ;
343+ `
344+ : undefined
345+ }
346+ >
280347 < MainMedia
281348 format = { format }
282349 elements = { article . mainMediaElements }
@@ -301,6 +368,11 @@ export const StandardLayout = (props: WebProps | AppProps) => {
301368 area = "title"
302369 layoutType = { layoutType }
303370 element = "aside"
371+ css = { css `
372+ display : flex;
373+ flex-direction : column;
374+ justify-content : space-between;
375+ ` }
304376 >
305377 < ArticleTitle
306378 format = { format }
@@ -310,19 +382,70 @@ export const StandardLayout = (props: WebProps | AppProps) => {
310382 guardianBaseURL = { article . guardianBaseURL }
311383 isMatch = { ! ! footballMatchUrl }
312384 />
385+ { displayAvatarUrl && (
386+ < Hide until = "leftCol" >
387+ < StraightLines
388+ count = { 8 }
389+ cssOverrides = { css `
390+ display : block;
391+ ` }
392+ color = { themePalette ( '--straight-lines' ) }
393+ />
394+ </ Hide >
395+ ) }
313396 </ GridItem >
314- < GridItem area = "headline" layoutType = { layoutType } >
315- < ArticleHeadline
316- format = { format }
317- headlineString = { article . headline }
318- tags = { article . tags }
319- byline = { article . byline }
320- webPublicationDateDeprecated = {
321- article . webPublicationDateDeprecated
322- }
323- starRating = { article . starRating }
324- />
325- </ GridItem >
397+ { displayAvatarUrl ? (
398+ < GridItem area = "headline" layoutType = { layoutType } >
399+ < div css = { avatarHeadlineWrapper } >
400+ < ArticleHeadline
401+ format = { format }
402+ headlineString = { article . headline }
403+ tags = { article . tags }
404+ byline = { article . byline }
405+ webPublicationDateDeprecated = {
406+ article . webPublicationDateDeprecated
407+ }
408+ hasAvatar = { displayAvatarUrl }
409+ starRating = { article . starRating }
410+ />
411+
412+ < div >
413+ { ! ! avatarUrl && (
414+ < div css = { avatarPositionStyles } >
415+ < ContributorAvatar
416+ imageSrc = { avatarUrl }
417+ imageAlt = {
418+ article . byline ?? ''
419+ }
420+ />
421+ </ div >
422+ ) }
423+ < StraightLines
424+ count = { 8 }
425+ cssOverrides = { css `
426+ display : block;
427+ ` }
428+ color = { themePalette (
429+ '--straight-lines' ,
430+ ) }
431+ />
432+ </ div >
433+ </ div >
434+ </ GridItem >
435+ ) : (
436+ < GridItem area = "headline" layoutType = { layoutType } >
437+ < ArticleHeadline
438+ format = { format }
439+ headlineString = { article . headline }
440+ tags = { article . tags }
441+ byline = { article . byline }
442+ webPublicationDateDeprecated = {
443+ article . webPublicationDateDeprecated
444+ }
445+ starRating = { article . starRating }
446+ />
447+ </ GridItem >
448+ ) }
326449 < GridItem area = "standfirst" layoutType = { layoutType } >
327450 < Standfirst
328451 format = { format }
@@ -339,12 +462,12 @@ export const StandardLayout = (props: WebProps | AppProps) => {
339462 format . theme === ArticleSpecial . Labs &&
340463 format . design !== ArticleDesign . Video ? (
341464 < GuardianLabsLines />
342- ) : (
465+ ) : ! displayAvatarUrl ? (
343466 < DecideLines
344467 format = { format }
345468 color = { themePalette ( '--article-border' ) }
346469 />
347- ) }
470+ ) : null }
348471 </ div >
349472 { isApps ? (
350473 < >
0 commit comments