diff mbox series

[bug#48637] website: Add publications page

Message ID 4BtcqLwBY7LgeYhUd41lhUUAcT5n2Zn9OTJUzGswTuBpLla8nBHyrQ3kJzwEMI9E7MPEnr6wBqSeCMqByldZ6i5jPR8B7ZtEi0XhwextyQo=@protonmail.com
State Accepted
Headers show
Series [bug#48637] website: Add publications page | expand

Checks

Context Check Description
cbaines/applying patch fail View Laminar job
cbaines/issue success View issue

Commit Message

Luis Felipe May 24, 2021, 11:35 p.m. UTC
Hi,

This patch adds a new Publications page to the website.


---
Luis Felipe López Acevedo
https://luis-felipe.gitlab.io/

Comments

pelzflorian (Florian Pelz) May 25, 2021, 1:44 p.m. UTC | #1
On Mon, May 24, 2021 at 11:35:24PM +0000, Luis Felipe via Guix-patches via wrote:
> Hi,
> 
> This patch adds a new Publications page to the website.

Nice!

I could not test yet, but maybe make the date string in

> +(define (publication->shtml publication)
> +  "Return an SHTML representation of the given publication object.
> +
> +   PUBLICATION (<publication>)
> +     A publication object as defined in (apps media types)."
> +  `(a
> +    (@ (class "publication-preview")
> +       (href ,(publication-url publication)))
> +
> +    (h3
> +     (@ (lang ,(publication-language publication))
> +        (class "publication-title"))
> +     ,(publication-title publication))
> +
> +    (p
> +     (@ (class "publication-info"))
> +     ,(G_ `("Published "
> +            ,(date->string (publication-date publication) "~b ~d, ~Y")

translatable like website/apps/blog/templates/components.scm line 33.


> +(define (publication-list-t publications)
> +  "Return the Publication list page in SHTML.
> +
> +   PUBLICATIONS (list of <publication>)
> +     See the (apps media types) module for information on the
> +     <publication> type."
> +  (theme
> +   #:title (C_ "webpage title" '("Publications"))
> +   #:description
> +   (G_ "A list of written publications about GNU Guix.")
> +   #:keywords
> +   ;; TRANSLATORS: |-separated list of webpage keywords.
> +   (string-split (G_ "Publications|Papers") #\|)

I don’t know, but should there really be only none of the other pages’
SEO keywords?  All other occurrences of #:keywords begin with

GNU|Linux|Unix|Free software


Regards,
Florian
Luis Felipe May 25, 2021, 2:37 p.m. UTC | #2
‐‐‐‐‐‐‐ Original Message ‐‐‐‐‐‐‐
On Tuesday, May 25, 2021 1:44 PM, pelzflorian (Florian Pelz) <pelzflorian@pelzflorian.de> wrote:

> On Mon, May 24, 2021 at 11:35:24PM +0000, Luis Felipe via Guix-patches via wrote:
>
> > Hi,
> > This patch adds a new Publications page to the website.
>
> Nice!
>
> I could not test yet, but maybe make the date string in
>
> > +(define (publication->shtml publication)
> >
> > -   "Return an SHTML representation of the given publication object.
> > -
> > -   PUBLICATION (<publication>)
> > -       A publication object as defined in (apps media types)."
> >
> >
> > -   `(a
> > -   (@ (class "publication-preview")
> > -         (href ,(publication-url publication)))
> >
> >
> > -
> > -   (h3
> > -       (@ (lang ,(publication-language publication))
> >
> >
> > -          (class "publication-title"))
> >
> >
> > -       ,(publication-title publication))
> >
> >
> > -
> > -   (p
> > -       (@ (class "publication-info"))
> >
> >
> > -       ,(G_ `("Published "
> >
> >
> > -              ,(date->string (publication-date publication) "~b ~d, ~Y")
> >
> >
>
> translatable like website/apps/blog/templates/components.scm line 33.

Yeah. Would it as follows? (I still struggle with i18n):

(p
 (@ (class "publication-info"))
 ,(G_ `("Published "
        ,(date->string (publication-date publication)
                       (C_ "SRFI-19 date->string format" "~b ~d, ~Y"))
        " by "
        ,(string-join (publication-authors publication) ", "))))

Also, I think the delimiter for "string-join" in the list of authors should be translated because that comma is not used in Japanese, for example. So would it be ok like this:

(p
 (@ (class "publication-info"))
 ,(G_ `("Published "
        ,(date->string (publication-date publication)
                       (C_ "SRFI-19 date->string format" "~b ~d, ~Y"))
        " by "
        ,(string-join (publication-authors publication)
                      (C_ "Separator of items in a list" ", ")))))


I'm still unsure about when I should use a context and when a TRANSLATORS comment...


> > +(define (publication-list-t publications)
> >
> > -   "Return the Publication list page in SHTML.
> > -
> > -   PUBLICATIONS (list of <publication>)
> > -       See the (apps media types) module for information on the
> >
> >
> > -       <publication> type."
> >
> >
> > -   (theme
> > -   #:title (C_ "webpage title" '("Publications"))
> > -   #:description
> > -   (G_ "A list of written publications about GNU Guix.")
> > -   #:keywords
> > -   ;; TRANSLATORS: |-separated list of webpage keywords.
> > -   (string-split (G_ "Publications|Papers") #\|)
>
> I don’t know, but should there really be only none of the other pages’
> SEO keywords? All other occurrences of #:keywords begin with
>
> GNU|Linux|Unix|Free software

Hmm, I don't know... Last time I checked, I think I saw that search engines care more about the page content than meta keywords. So I don't think it would make a difference.

Also, if it turns out to be important to repeat these keywords in all pages, maybe it would be better to add these by default in (apps base templates theme).
pelzflorian (Florian Pelz) May 25, 2021, 6:21 p.m. UTC | #3
On Tue, May 25, 2021 at 02:37:03PM +0000, Luis Felipe wrote:
> Yeah. Would it as follows? (I still struggle with i18n):
> 
> (p
>  (@ (class "publication-info"))
>  ,(G_ `("Published "
>         ,(date->string (publication-date publication)
>                        (C_ "SRFI-19 date->string format" "~b ~d, ~Y"))
>         " by "
>         ,(string-join (publication-authors publication) ", "))))

It would need to be

(p
 (@ (class "publication-info"))
 ,(let ((format (C_ "SRFI-19 date->string format" "~b ~d, ~Y")))
    (G_ `("Published "
          ,(date->string (publication-date publication) format)
          " by "
          ,(string-join (publication-authors publication) ", ")
          ""))))

The let is needed because C_ cannot be nested inside G_ because the
sexp-xgettext script parses Scheme files in only one pass currently.
(And I’m not motivated enough to make it go back to parse the nested
C_ because nesting is seldom needed.)

The "" at the end is needed so (string-join …) gets its own XML
element that can be moved by translators.

Without "":

msgid "Published <1/> by "

With "":

msgid "Published <1/> by <2/>"



> Also, I think the delimiter for "string-join" in the list of authors
> should be translated because that comma is not used in Japanese, for
> example. So would it be ok like this:
> […]
>         ,(string-join (publication-authors publication)
>                       (C_ "Separator of items in a list" ", ")))))

Yes, you are very right, although as above please move it to the let.
Then it can become 、 or と or whatever; I don’t know what is common
in Japan.


> I'm still unsure about when I should use a context and when a
> TRANSLATORS comment...

A msgctxt should be used if another ", " string with different
translations might be added.  I would use a context, I think, but
maybe it is an abundance of caution.

Regards,
Florian
pelzflorian (Florian Pelz) May 25, 2021, 6:35 p.m. UTC | #4
On Tue, May 25, 2021 at 02:37:03PM +0000, Luis Felipe wrote:
> On Tuesday, May 25, 2021 1:44 PM, pelzflorian (Florian Pelz) <pelzflorian@pelzflorian.de> wrote:
> > On Mon, May 24, 2021 at 11:35:24PM +0000, Luis Felipe via Guix-patches via wrote:
> > > -   (theme
> > > -   #:title (C_ "webpage title" '("Publications"))
> > > -   #:description
> > > -   (G_ "A list of written publications about GNU Guix.")
> > > -   #:keywords
> > > -   ;; TRANSLATORS: |-separated list of webpage keywords.
> > > -   (string-split (G_ "Publications|Papers") #\|)
> >
> > I don’t know, but should there really be only none of the other pages’
> > SEO keywords? All other occurrences of #:keywords begin with
> >
> > GNU|Linux|Unix|Free software
>
> Hmm, I don't know... Last time I checked, I think I saw that search
> engines care more about the page content than meta keywords. So I
> don't think it would make a difference.
>
> Also, if it turns out to be important to repeat these keywords in
> all pages, maybe it would be better to add these by default in (apps
> base templates theme).

I did not know and just thought we should cargo-cult it.  Does it
matter?  Maybe @sirgazil has an opinion?

Regards,
Florian
Luis Felipe May 25, 2021, 7:14 p.m. UTC | #5
On Tuesday, May 25, 2021 6:35 PM, pelzflorian (Florian Pelz) <pelzflorian@pelzflorian.de> wrote:

> On Tue, May 25, 2021 at 02:37:03PM +0000, Luis Felipe wrote:
>
> > On Tuesday, May 25, 2021 1:44 PM, pelzflorian (Florian Pelz) pelzflorian@pelzflorian.de wrote:
> >
> > > On Mon, May 24, 2021 at 11:35:24PM +0000, Luis Felipe via Guix-patches via wrote:
> > >
> > > > -   (theme
> > > > -   #:title (C_ "webpage title" '("Publications"))
> > > > -   #:description
> > > > -   (G_ "A list of written publications about GNU Guix.")
> > > > -   #:keywords
> > > > -   ;; TRANSLATORS: |-separated list of webpage keywords.
> > > > -   (string-split (G_ "Publications|Papers") #\|)
> > >
> > > I don’t know, but should there really be only none of the other pages’
> > > SEO keywords? All other occurrences of #:keywords begin with
> > > GNU|Linux|Unix|Free software
> >
> > Hmm, I don't know... Last time I checked, I think I saw that search
> > engines care more about the page content than meta keywords. So I
> > don't think it would make a difference.
> > Also, if it turns out to be important to repeat these keywords in
> > all pages, maybe it would be better to add these by default in (apps
> > base templates theme).
>
> I did not know and just thought we should cargo-cult it. Does it
> matter? Maybe @sirgazil has an opinion?

He, he, I'm sirgazil :)
Luis Felipe May 25, 2021, 7:46 p.m. UTC | #6
On Tuesday, May 25, 2021 6:21 PM, pelzflorian (Florian Pelz) <pelzflorian@pelzflorian.de> wrote:

> On Tue, May 25, 2021 at 02:37:03PM +0000, Luis Felipe wrote:
>
> > Yeah. Would it as follows? (I still struggle with i18n):
> > (p
> > (@ (class "publication-info"))
> > ,(G_ `("Published "
> > ,(date->string (publication-date publication)
> > (C_ "SRFI-19 date->string format" "~b ~d, ~Y"))
> > " by "
> > ,(string-join (publication-authors publication) ", "))))
>
> It would need to be
>
> (p
> (@ (class "publication-info"))
> ,(let ((format (C_ "SRFI-19 date->string format" "~b ~d, ~Y")))
>
>     (G_ `("Published "
>           ,(date->string (publication-date publication) format)
>
>           " by "
>           ,(string-join (publication-authors publication) ", ")
>           ""))))
>
>
> The let is needed because C_ cannot be nested inside G_ because the
> sexp-xgettext script parses Scheme files in only one pass currently.
> (And I’m not motivated enough to make it go back to parse the nested
> C_ because nesting is seldom needed.)
>
> The "" at the end is needed so (string-join …) gets its own XML
> element that can be moved by translators.
>
> Without "":
>
> msgid "Published <1/> by "
>
> With "":
>
> msgid "Published <1/> by <2/>"

You told me that before but I forgot :P


> > Also, I think the delimiter for "string-join" in the list of authors
> > should be translated because that comma is not used in Japanese, for
> > example. So would it be ok like this:
> > […]
> > ,(string-join (publication-authors publication)
> > (C_ "Separator of items in a list" ", ")))))
>
> Yes, you are very right, although as above please move it to the let.
> Then it can become 、 or と or whatever; I don’t know what is common
> in Japan.
>
> > I'm still unsure about when I should use a context and when a
> > TRANSLATORS comment...
>
> A msgctxt should be used if another ", " string with different
> translations might be added. I would use a context, I think, but
> maybe it is an abundance of caution.

Ok, so I moved both the date and the list authors to the definitions part of a let:

(define (publication->shtml publication)
  "Return an SHTML representation of the given publication object.

   PUBLICATION (<publication>)
     A publication object as defined in (apps media types)."
  (let ((date
         (date->string (publication-date publication)
                       (C_ "SRFI-19 date->string format" "~b ~d, ~Y")))
        (authors
         (string-join (publication-authors publication)
                      (C_ "Separator for authors in a list" ", "))))

    `(a
      (@ (class "publication-preview")
         (href ,(publication-url publication)))

      (h3
       (@ (lang ,(publication-language publication))
          (class "publication-title"))
       ,(publication-title publication))

      (p
       (@ (class "publication-info"))
       ,(G_ `("Published " ,date " by " ,authors ""))))))

Does it look right?
Julien Lepiller May 25, 2021, 7:58 p.m. UTC | #7
Do we need to have a list for authors? Couldn't we have a single translatable string with all the authors, so it's easier to translate than having to translate a separator? I mean you could have weird rujes on separators. Even in English, you'd need an "and" for the last element, unless there's one author. Giving the full list of authors to translate gives the most freedom to translators.

Also, please give context/comment to the last one, because it's not easy to guess what <1> in "Published <1> by <2>" would be :)

Le 25 mai 2021 15:46:17 GMT-04:00, Luis Felipe via Guix-patches via <guix-patches@gnu.org> a écrit :
>On Tuesday, May 25, 2021 6:21 PM, pelzflorian (Florian Pelz)
><pelzflorian@pelzflorian.de> wrote:
>
>> On Tue, May 25, 2021 at 02:37:03PM +0000, Luis Felipe wrote:
>>
>> > Yeah. Would it as follows? (I still struggle with i18n):
>> > (p
>> > (@ (class "publication-info"))
>> > ,(G_ `("Published "
>> > ,(date->string (publication-date publication)
>> > (C_ "SRFI-19 date->string format" "~b ~d, ~Y"))
>> > " by "
>> > ,(string-join (publication-authors publication) ", "))))
>>
>> It would need to be
>>
>> (p
>> (@ (class "publication-info"))
>> ,(let ((format (C_ "SRFI-19 date->string format" "~b ~d, ~Y")))
>>
>>     (G_ `("Published "
>>           ,(date->string (publication-date publication) format)
>>
>>           " by "
>>           ,(string-join (publication-authors publication) ", ")
>>           ""))))
>>
>>
>> The let is needed because C_ cannot be nested inside G_ because the
>> sexp-xgettext script parses Scheme files in only one pass currently.
>> (And I’m not motivated enough to make it go back to parse the nested
>> C_ because nesting is seldom needed.)
>>
>> The "" at the end is needed so (string-join …) gets its own XML
>> element that can be moved by translators.
>>
>> Without "":
>>
>> msgid "Published <1/> by "
>>
>> With "":
>>
>> msgid "Published <1/> by <2/>"
>
>You told me that before but I forgot :P
>
>
>> > Also, I think the delimiter for "string-join" in the list of
>authors
>> > should be translated because that comma is not used in Japanese,
>for
>> > example. So would it be ok like this:
>> > […]
>> > ,(string-join (publication-authors publication)
>> > (C_ "Separator of items in a list" ", ")))))
>>
>> Yes, you are very right, although as above please move it to the let.
>> Then it can become 、 or と or whatever; I don’t know what is common
>> in Japan.
>>
>> > I'm still unsure about when I should use a context and when a
>> > TRANSLATORS comment...
>>
>> A msgctxt should be used if another ", " string with different
>> translations might be added. I would use a context, I think, but
>> maybe it is an abundance of caution.
>
>Ok, so I moved both the date and the list authors to the definitions
>part of a let:
>
>(define (publication->shtml publication)
>  "Return an SHTML representation of the given publication object.
>
>   PUBLICATION (<publication>)
>     A publication object as defined in (apps media types)."
>  (let ((date
>         (date->string (publication-date publication)
>                       (C_ "SRFI-19 date->string format" "~b ~d, ~Y")))
>        (authors
>         (string-join (publication-authors publication)
>                      (C_ "Separator for authors in a list" ", "))))
>
>    `(a
>      (@ (class "publication-preview")
>         (href ,(publication-url publication)))
>
>      (h3
>       (@ (lang ,(publication-language publication))
>          (class "publication-title"))
>       ,(publication-title publication))
>
>      (p
>       (@ (class "publication-info"))
>       ,(G_ `("Published " ,date " by " ,authors ""))))))
>
>Does it look right?
Luis Felipe May 25, 2021, 8:16 p.m. UTC | #8
On Tuesday, May 25, 2021 7:58 PM, Julien Lepiller <julien@lepiller.eu> wrote:

> Do we need to have a list for authors? Couldn't we have a single translatable string with all the authors, so it's easier to translate than having to translate a separator? I mean you could have weird rujes on separators. Even in English, you'd need an "and" for the last element, unless there's one author. Giving the full list of authors to translate gives the most freedom to translators.

Hmm... As a scripter I'd prefer to use the list, but as a translator, I'd like to have complete control of the string... For the benefit of the latter, I think it's ok to use the string instead. So, when instancing a Publication record, the authors field would be simply like this?

#:authors (G_ "Jane Roe, Eva Lua and Luis Felipe")


> Also, please give context/comment to the last one, because it's not easy to guess what <1> in "Published <1> by <2>" would be :)

That's what I thought, and I was about to ask if it was possible to have <date> and <authors> instead of <1> and <2>. I guess not...

I need a course on the i18n system :)
Julien Lepiller May 26, 2021, 2:03 a.m. UTC | #9
Le 25 mai 2021 16:16:42 GMT-04:00, Luis Felipe <luis.felipe.la@protonmail.com> a écrit :
>On Tuesday, May 25, 2021 7:58 PM, Julien Lepiller <julien@lepiller.eu>
>wrote:
>
>> Do we need to have a list for authors? Couldn't we have a single
>translatable string with all the authors, so it's easier to translate
>than having to translate a separator? I mean you could have weird rujes
>on separators. Even in English, you'd need an "and" for the last
>element, unless there's one author. Giving the full list of authors to
>translate gives the most freedom to translators.
>
>Hmm... As a scripter I'd prefer to use the list, but as a translator,
>I'd like to have complete control of the string... For the benefit of
>the latter, I think it's ok to use the string instead. So, when
>instancing a Publication record, the authors field would be simply like
>this?
>
>#:authors (G_ "Jane Roe, Eva Lua and Luis Felipe")

I think so, but I think we'll have to test, because I don't really know the i18n process either for the website ^^'

>
>
>> Also, please give context/comment to the last one, because it's not
>easy to guess what <1> in "Published <1> by <2>" would be :)
>
>That's what I thought, and I was about to ask if it was possible to
>have <date> and <authors> instead of <1> and <2>. I guess not...
>
>I need a course on the i18n system :)
pelzflorian (Florian Pelz) May 26, 2021, 10 a.m. UTC | #10
On Tue, May 25, 2021 at 10:03:01PM -0400, Julien Lepiller wrote:
> Le 25 mai 2021 16:16:42 GMT-04:00, Luis Felipe <luis.felipe.la@protonmail.com> a écrit :
> >#:authors (G_ "Jane Roe, Eva Lua and Luis Felipe")
> 
> I think so, but I think we'll have to test, because I don't really know the i18n process either for the website ^^'

Yes, it is correct and the best way, but testing by running the
sexp-xgettext script is good too.


> >That's what I thought, and I was about to ask if it was possible to
> >have <date> and <authors> instead of <1> and <2>. I guess not...
> >
> >I need a course on the i18n system :)

I wanted the sexp-xgettext i18n system so developers would not have to
add a tag name to every element …  Well, we can use regexp helper
function find-empty-element like website/sexp-xgettext.scm uses at the
beginning of the deconstruct procedure:

(define (find-empty-element msgstr name)
  "Return the regex match structure for the empty tag for XML
element of type NAME inside MSGSTR.  If the element does not exist or
is more than the empty tag, #f is returned."
  (string-match (string-append "<" (regexp-quote name) "/>") msgstr))

and call it on a translation with G_, but I would prefer not to
increase code complexity in the website.

Regards,
Florian
diff mbox series

Patch

From eb36bae9f13b7825b22303e1efe6fd37c563ccdd Mon Sep 17 00:00:00 2001
From: Luis Felipe <luis.felipe.la@protonmail.com>
Date: Mon, 24 May 2021 17:54:17 -0500
Subject: [PATCH] website: Add publications page.

This page lists different kinds of written publications that talk about
Guix.

* website/apps/base/templates/components.scm (navbar): Link to the new
page from the Media menu.
* website/apps/media/builder.scm (publication-list-builder): New helper
builder.
(builder): Use the new helper builder.
* website/apps/media/data.scm (publications): New data set.
* website/apps/media/templates/components.scm (publication->shtml): New
component.
* website/apps/media/templates/publication-list.scm: New template.
* website/apps/media/types.scm (<publication>): New record type.
* website/static/base/img/link-arrow-shaper.svg: New image.
* website/static/media/css/publications.css: New style sheet.
---
 website/apps/base/templates/components.scm    | 14 +++-
 website/apps/media/builder.scm                | 19 ++++--
 website/apps/media/data.scm                   | 25 +++++++
 website/apps/media/templates/components.scm   | 25 ++++++-
 .../apps/media/templates/publication-list.scm | 46 +++++++++++++
 website/apps/media/types.scm                  | 52 ++++++++++++++-
 website/static/base/img/link-arrow-shaper.svg | 66 +++++++++++++++++++
 website/static/media/css/publications.css     | 35 ++++++++++
 8 files changed, 274 insertions(+), 8 deletions(-)
 create mode 100644 website/apps/media/templates/publication-list.scm
 create mode 100644 website/static/base/img/link-arrow-shaper.svg
 create mode 100644 website/static/media/css/publications.css

diff --git a/website/apps/base/templates/components.scm b/website/apps/base/templates/components.scm
index b615f98..e2a74d3 100644
--- a/website/apps/base/templates/components.scm
+++ b/website/apps/base/templates/components.scm
@@ -461,8 +461,18 @@  manual.
       ,(menu-dropdown #:label (C_ "website menu" "Media") #:active-item active-item
         #:items
         (list
-         (C_ "website menu" (menu-item #:label "Videos" #:active-item active-item #:url (guix-url "videos/")))
-         (C_ "website menu" (menu-item #:label "Screenshots" #:active-item active-item #:url (guix-url "screenshots/")))))
+         (C_ "website menu"
+             (menu-item #:label "Videos"
+                        #:active-item active-item
+                        #:url (guix-url "videos/")))
+         (C_ "website menu"
+             (menu-item #:label "Screenshots"
+                        #:active-item active-item
+                        #:url (guix-url "screenshots/")))
+         (C_ "website menu"
+             (menu-item #:label "Publications"
+                        #:active-item active-item
+                        #:url (guix-url "publications/")))))
 
       ,(C_ "website menu" (menu-item #:label "Donate" #:active-item active-item #:url (guix-url "donate/")))
 
diff --git a/website/apps/media/builder.scm b/website/apps/media/builder.scm
index 53378e6..c270db5 100644
--- a/website/apps/media/builder.scm
+++ b/website/apps/media/builder.scm
@@ -6,11 +6,13 @@ 
 (define-module (apps media builder)
   #:use-module (apps aux system)
   #:use-module (apps media data)
+  #:use-module (apps media templates publication-list)
   #:use-module (apps media templates screenshot)
   #:use-module (apps media templates screenshots-overview)
   #:use-module (apps media templates video)
   #:use-module (apps media templates video-list)
   #:use-module (apps media types)
+  #:use-module (haunt artifact)
   #:use-module (haunt html)
   #:use-module (haunt page)
   #:use-module (haunt utils)
@@ -37,11 +39,13 @@ 
      A list of post objects that represent articles from the blog. See
      Haunt <post> objects for more information.
 
-   RETURN (list of <page>)
-     A list of page objects that represent the web resources of the
-     application. See Haunt <page> objects for more information."
+   RETURN (list of <artifact> and <page>)
+     A list of objects that represent the web resources of the
+     application. See Haunt <artifact> and <page> objects for more
+     information."
   (flatten
-   (list (screenshots-overview-builder)
+   (list (publication-list-builder)
+         (screenshots-overview-builder)
          (screenshots-builder)
          (videos-builder)
          (video-list-builder))))
@@ -51,6 +55,13 @@ 
 ;;; Helper builders.
 ;;;
 
+(define (publication-list-builder)
+  "Return a Haunt artifact representing the publications page."
+  (serialized-artifact (url-path-join "publications" "index.html")
+                       (publication-list-t publications)
+                       sxml->html))
+
+
 (define (screenshots-builder)
   "Return a list of Haunt pages representing screenshot pages."
   (map
diff --git a/website/apps/media/data.scm b/website/apps/media/data.scm
index 9b1e2c5..8f4f2b0 100644
--- a/website/apps/media/data.scm
+++ b/website/apps/media/data.scm
@@ -9,6 +9,7 @@ 
   #:use-module (apps media types)
   #:use-module (srfi srfi-19)
   #:export (playlists
+            publications
             screenshots))
 
 
@@ -98,6 +99,30 @@  distribution."))
      #:last-updated (string->date "2020-03-28T16:00:00" "~Y-~m-~dT~H:~M:~S")))))
 
 
+(define publications
+  (list
+   (publication
+    #:title "Functional Package Management with Guix"
+    #:url "https://arxiv.org/abs/1305.4584"
+    #:authors (list "Ludovic Courtès")
+    #:date (string->date "2013-05-20" "~Y-~m-~d"))
+   (publication
+    #:title "Reproducible and User-Controlled Software Environments in HPC with Guix"
+    #:url "https://hal.inria.fr/hal-01161771/en"
+    #:authors (list "Ludovic Courtès" "Ricardo Wurmus")
+    #:date (string->date "2015-07-25" "~Y-~m-~d"))
+   (publication
+    #:title "Code Staging in GNU Guix"
+    #:url "https://arxiv.org/abs/1709.00833"
+    #:authors (list "Ludovic Courtès")
+    #:date (string->date "2017-09-04" "~Y-~m-~d"))
+   (publication
+    #:title "Guix: A most advanced operating system"
+    #:url "https://ambrevar.xyz/guix-advance/index.html"
+    #:authors (list "Pierre Neidhardt")
+    #:date (string->date "2019-01-14" "~Y-~m-~d"))))
+
+
 (define screenshots
   (list
    (screenshot
diff --git a/website/apps/media/templates/components.scm b/website/apps/media/templates/components.scm
index d928d23..e72b380 100644
--- a/website/apps/media/templates/components.scm
+++ b/website/apps/media/templates/components.scm
@@ -14,7 +14,8 @@ 
   #:use-module (apps media types)
   #:use-module (apps media utils)
   #:use-module (srfi srfi-19)
-  #:export (screenshot->shtml
+  #:export (publication->shtml
+            screenshot->shtml
             screenshots-box
             video->shtml
             video-content
@@ -25,6 +26,28 @@ 
 ;;; Components.
 ;;;
 
+(define (publication->shtml publication)
+  "Return an SHTML representation of the given publication object.
+
+   PUBLICATION (<publication>)
+     A publication object as defined in (apps media types)."
+  `(a
+    (@ (class "publication-preview")
+       (href ,(publication-url publication)))
+
+    (h3
+     (@ (lang ,(publication-language publication))
+        (class "publication-title"))
+     ,(publication-title publication))
+
+    (p
+     (@ (class "publication-info"))
+     ,(G_ `("Published "
+            ,(date->string (publication-date publication) "~b ~d, ~Y")
+            " by "
+            ,(string-join (publication-authors publication) ", "))))))
+
+
 (define (screenshot->shtml shot)
   "Return an SHTML representation of the given screenshot object.
 
diff --git a/website/apps/media/templates/publication-list.scm b/website/apps/media/templates/publication-list.scm
new file mode 100644
index 0000000..44847c7
--- /dev/null
+++ b/website/apps/media/templates/publication-list.scm
@@ -0,0 +1,46 @@ 
+;;; GNU Guix web site
+;;; Public domain 2021 Luis Felipe López Acevedo
+
+(define-module (apps media templates publication-list)
+  #:use-module (apps base templates theme)
+  #:use-module (apps base types)
+  #:use-module (apps base utils)
+  #:use-module (apps i18n)
+  #:use-module (apps media templates components)
+  #:export (publication-list-t))
+
+
+(define (publication-list-t publications)
+  "Return the Publication list page in SHTML.
+
+   PUBLICATIONS (list of <publication>)
+     See the (apps media types) module for information on the
+     <publication> type."
+  (theme
+   #:title (C_ "webpage title" '("Publications"))
+   #:description
+   (G_ "A list of written publications about GNU Guix.")
+   #:keywords
+   ;; TRANSLATORS: |-separated list of webpage keywords.
+   (string-split (G_ "Publications|Papers") #\|)
+   #:active-menu-item (C_ "website menu" "Publications")
+   #:css (list
+	  (guix-url "static/base/css/page.css")
+	  (guix-url "static/media/css/publications.css"))
+   #:crumbs (list (crumb (C_ "website menu" "Publications") "./"))
+   #:content
+   `(main
+     (section
+      (@ (class "page"))
+      ,(G_ `(h2 "Publications"))
+
+      ,(G_
+        `(p
+          (@ (class "centered-block limit-width"))
+
+          "The following is a list of written materials that talk about GNU Guix. It is a diverse collection of writings, from blog posts to grey literature to academic and research papers."))
+
+      (div
+       (@ (class "publication-list centered-block limit-width"))
+
+       ,@(map publication->shtml publications))))))
diff --git a/website/apps/media/types.scm b/website/apps/media/types.scm
index 50c0989..9054141 100644
--- a/website/apps/media/types.scm
+++ b/website/apps/media/types.scm
@@ -5,7 +5,14 @@ 
 
 (define-module (apps media types)
   #:use-module (srfi srfi-9)
-  #:export (screenshot
+  #:export (publication
+            publication?
+            publication-authors
+            publication-date
+            publication-language
+            publication-title
+            publication-url
+            screenshot
             screenshot?
             screenshot-caption
             screenshot-image
@@ -32,6 +39,49 @@ 
 ;;; Data types.
 ;;;
 
+;;; Publication (record type)
+;;; -------------------------
+;;;
+;;; A publication object represents a written material that talks about
+;;; GNU Guix.
+;;;
+;;; Objects of this type can be created with the "publication" procedure
+;;; (see Helper procedures below).
+;;;
+;;; Fields:
+;;;
+;;; title (string)
+;;;   The title of the publication.
+;;;
+;;; url (string)
+;;;   A URL to the publication.
+;;;
+;;; authors (list of string)
+;;;   The names of the authors.
+;;;
+;;; date (date)
+;;;   The date of publication.
+;;;
+;;; language (string)
+;;;   IETF language tag corresponding to the language in which the
+;;;   publication is written.
+;;;
+(define-record-type <publication>
+  (make-publication title url authors date language)
+  publication?
+  (title publication-title)
+  (url publication-url)
+  (authors publication-authors)
+  (date publication-date)
+  (language publication-language))
+
+;;; Helper procedures.
+
+(define* (publication #:key title url authors date (language "en"))
+  "Return a <publication> object with the given attributes."
+  (make-publication title url authors date language))
+
+
 ;;; Screenshot (record type)
 ;;; ------------------------
 ;;;
diff --git a/website/static/base/img/link-arrow-shaper.svg b/website/static/base/img/link-arrow-shaper.svg
new file mode 100644
index 0000000..834fd84
--- /dev/null
+++ b/website/static/base/img/link-arrow-shaper.svg
@@ -0,0 +1,66 @@ 
+<?xml version="1.0" encoding="UTF-8" standalone="no"?>
+<svg
+   xmlns:dc="http://purl.org/dc/elements/1.1/"
+   xmlns:cc="http://creativecommons.org/ns#"
+   xmlns:rdf="http://www.w3.org/1999/02/22-rdf-syntax-ns#"
+   xmlns:svg="http://www.w3.org/2000/svg"
+   xmlns="http://www.w3.org/2000/svg"
+   xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
+   xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
+   version="1.1"
+   width="50"
+   height="100"
+   id="RSSicon"
+   viewBox="0 0 100 200"
+   inkscape:version="1.0.2 (e86c870879, 2021-01-15)"
+   sodipodi:docname="link-arrow-shaper.svg"
+   inkscape:export-filename="feed-bg-whitepng.png"
+   inkscape:export-xdpi="90"
+   inkscape:export-ydpi="90">
+  <metadata
+     id="metadata34">
+    <rdf:RDF>
+      <cc:Work
+         rdf:about="">
+        <dc:format>image/svg+xml</dc:format>
+        <dc:type
+           rdf:resource="http://purl.org/dc/dcmitype/StillImage" />
+        <dc:title />
+      </cc:Work>
+    </rdf:RDF>
+  </metadata>
+  <sodipodi:namedview
+     pagecolor="#ffffff"
+     bordercolor="#666666"
+     borderopacity="1"
+     objecttolerance="10"
+     gridtolerance="10"
+     guidetolerance="10"
+     inkscape:pageopacity="0"
+     inkscape:pageshadow="2"
+     inkscape:window-width="1920"
+     inkscape:window-height="1016"
+     id="namedview32"
+     showgrid="false"
+     inkscape:showpageshadow="false"
+     inkscape:zoom="1"
+     inkscape:cx="94.060188"
+     inkscape:cy="157.85589"
+     inkscape:window-x="0"
+     inkscape:window-y="27"
+     inkscape:window-maximized="1"
+     inkscape:current-layer="RSSicon"
+     inkscape:snap-page="false"
+     inkscape:document-rotation="0"
+     fit-margin-top="0"
+     fit-margin-left="0"
+     fit-margin-right="0"
+     fit-margin-bottom="0" />
+  <defs
+     id="defs3" />
+  <path
+     id="rect88"
+     style="opacity:1;fill:#ffffff;stroke:none;stroke-width:3.77734"
+     d="M 0,-4 H 104 V 204 H 0 L 56,100 Z"
+     sodipodi:nodetypes="cccccc" />
+</svg>
diff --git a/website/static/media/css/publications.css b/website/static/media/css/publications.css
new file mode 100644
index 0000000..797918f
--- /dev/null
+++ b/website/static/media/css/publications.css
@@ -0,0 +1,35 @@ 
+.publication-list {
+    margin-top: 2em;
+}
+
+.publication-preview,
+.publication-preview:link,
+.publication-preview:visited {
+    display: block;
+    border-image: linear-gradient(to right, gray, transparent) 1;
+    border-style: none none solid none;
+    border-width: thin thick;
+    color: #4D4D4D;
+    padding: 20px 70px 20px 10px;
+    transition: border-width .2s cubic-bezier(.22,.61,.36,1);
+}
+
+.publication-preview:active,
+.publication-preview:focus,
+.publication-preview:hover {
+    background-color: gold;
+    background-image: url("/static/base/img/link-arrow-shaper.svg");
+    background-position: right;
+    background-repeat: no-repeat;
+    background-size: auto 100%;
+    border-image: linear-gradient(to right, #333, white, white) 1;
+    border-style: none none solid solid;
+}
+
+.publication-title {
+    margin: 0px;
+}
+
+.publication-info {
+    margin-bottom: 0px;
+}

base-commit: eeb5120135ad3f96f4aad2c7fa37364e6dca584b
-- 
2.31.1