How can I add images to a nametag, top left and top right from the main content?

I would like to create nametags like this:

This is the code I have so far.

// template
#set page(
  height: 16.5cm, 
  width: 18cm,
  margin: (left: 0cm, right: 0cm, top: 0cm, bottom: 0cm)
)
#set align(center)
#set text(16pt, fill: rgb("#444444"))
#let name-style(content) = {
  text(20pt, content)
}
#let author-box(content) = {
  set align(center + horizon)
  box(
    width: 100%,
    height: 100%,
    stroke: (paint: silver, thickness: 0.5pt, dash: "dashed"),
    content
  )
}

#let conf(
  authors: (),
  doc,
) = {
  let count = authors.len()
  let ncols = 2
  let nrows = 4
  
  grid(
    columns: (1fr,) * ncols,
    rows: (1fr,) * nrows,
    row-gutter: 0cm,
    ..authors.map(author => author-box([
         #author.name \
         #author.affiliation \
         #author.email
    ]))
  )
}

// values
#show: doc => conf(
  authors: (
    (
      name: "Theresa Tungsten",
      affiliation: "Artos Institute",
      email: "tung@artos.edu",
      logo_left: "images/logo1.png",
      logo_right: "images/logo2.png"
    ),
    (
      name: "Eugene Deklan",
      affiliation: "Honduras State",
      email: "e.deklan@hstate.hn",
    ),
    (
      name: "Eugene Deklan",
      affiliation: "Honduras State",
      email: "e.deklan@hstate.hn",
    ),
    (
      name: "Eugene Deklan",
      affiliation: "Honduras State",
      email: "e.deklan@hstate.hn",
    ),
    (
      name: "Eugene Deklan",
      affiliation: "Honduras State",
      email: "e.deklan@hstate.hn",
    ),
    (
      name: "Eugene Deklan",
      affiliation: "Honduras State",
      email: "e.deklan@hstate.hn",
    )
  ),
  doc,
)

It has been a struggle to get to this point. I would like to add two logos to the top left and top-right of each nametag.

  grid(
      columns: (1fr, 1fr),
      row-gutter: 0pt,
      align(left, image(author.logo_left, height: 6mm)),
      align(right, image(author.logo_right, height: 6mm))
    )

I can’t quite get this to work. Any help is appreciated.
Thanks!

Hi @mindlessgreen, welcome and thank you for your question! I have changed your post’s title to bring it in line with the question guidelines and thus make it easier to find in the future:

Good titles are questions you would ask your friend about Typst.

So this is the function that you want to “fix”:

Not a real issue, but using box() here is conceptually not the right tool. A grid cell is a block, and you don’t have a need (that I can see) to render a nametag embedded into a regular paragraph.

What you need to do to add the images is to put the label’s contents into a grid. You’ll also want the lines outside the grid so that it covers the whole nametag:

#let author-box(content) = block(
  // stroke around the grid
  stroke: (paint: silver, thickness: 0.5pt, dash: "dashed"),
  {
    // all cells are centered
    set align(center + horizon)

    grid(
      // same amount of space left and right for the images,
      // the center cell takes the rest
      columns: (1cm, 1fr, 1cm),  
      // the grid row fills the whole nametag's space
      rows: (1fr,),
      image("image.jpg"),
      content,
      image("image.jpg"),
    )
  },
)

As an aside, you can also render the lines (that are probably only for cutting and not really part of the name tags) as part of the outer grid (the one containing multiple nametags). That will simplify the function again:

#let author-box(content) = {
  set align(center + horizon)

  grid(
    columns: (1cm, 1fr, 1cm),  
    rows: (1fr,),
    image("image.jpg"),
    content,
    image("image.jpg"),
  )
}

#let conf(
  authors: (),
  doc,
) = {
  ...
  
  grid(
    ...
    stroke: (paint: silver, thickness: 0.5pt, dash: "dashed"),
    ...
  )
}
1 Like

Thank you! This has been very helpful. I have adjusted the grid so that the logos are organized better.

// template
#set page(
  height: 22cm, 
  width: 18cm,
  margin: (left: 0cm, right: 0cm, top: 0cm, bottom: 0cm)
)
#set text(16pt, font: "Barlow", fill: rgb("#444444"))
#let author-box(content) = block(
  pad(0.3cm,
    grid(
      columns: 1fr, 
      rows: (1fr, 8fr),
      grid(
        columns: (1fr, 1fr),
        rows: 1fr,
        align(left,
          image("images/logo1.png", height: 0.7cm)
        ),
        align(right,
          image("images/logo2.png", height: 0.6cm)
        ),
      ),
      align(center + horizon, content)
    )
  )
)

#let conf(
  authors: (),
  doc,
) = {
  let count = authors.len()
  let ncols = 2
  let nrows = 4

  grid(
    stroke: (paint: silver, thickness: 0.5pt, dash: "dashed"),
    columns: (1fr,) * ncols,
    rows: (1fr,) * nrows,
    row-gutter: 0cm,
    ..authors.map(author => author-box([
         #author.name \
         #author.affiliation \
         #author.email
    ]))
  )
}

// values
#show: doc => conf(
  authors: (
    (
      name: "Olivia Sterling",
      affiliation: "Quantum Research Institute",
      email: "o.sterling@quantumri.edu",
    ),
    (
      name: "Marcus Finnegan",
      affiliation: "Tech Innovators Corp",
      email: "m.finnegan@techinnovators.com",
    ),
    (
      name: "Ariella Knox",
      affiliation: "Global Solutions Inc.",
      email: "a.knox@globalsolutions.com",
    ),
    (
      name: "Theo Jansen",
      affiliation: "Netherlands Technology Hub",
      email: "t.jansen@nth.nl",
    ),
    (
      name: "Leila Summers",
      affiliation: "EcoLogic Research Foundation",
      email: "l.summers@ecologic.org",
    ),
    (
      name: "Jaxon Lee",
      affiliation: "Australian Science Network",
      email: "j.lee@australiascience.net.au",
    )
  ),
  doc,
)

Great! A few notes regarding your solution:

  • instead of putting a pad element within your block, you could probably also use block.inset.
  • if you wanted, you could use the colspan property to do the same with a single grid
  • if you want to have the upper grid take exactly the space needed by the images, you can make the first row’s height auto. As long as at least one row (and column) has a fr size, the grid will occupy the whole height/width.
1 Like