[Beginner] Creating a 3-column page

Hi everyone!

I just started to learn coding pretty recently and have some basic knowledge in html and css. Now for my internship, I’ll have to learn Grav and create a 3-column page like this one below:


I know how to do it using html and css (using flexbox), but I have no idea how to achieve this using grav…I also don’t have any previous knowledge on yaml and twig.
My questions are:

  • Do I need to create my own theme/template to achieve that?
  • So far I’ve tried to create a “3-column.yaml” file in quark’s blueprint folder and a “3-column.html.twig” file in quark’s themes folder, and put my css into the customer.css file. And it didn’t work. Is it the correct thing to do in order to achive the end effect?
    Any suggestions on how to achieve it?
    Thanks a lot for your help!

Monin

I suggest you start with the Bootstrap 4 theme. That’s a really commonly used framework and there are plenty of resources online to help you.

@monin,

  • If you stick to theme Quark, have a look at the grid framework of Spectre
  • If you go for theme Bootstrap 4, have a look at the grid framework of Bootstrap v4
    By the way, Bootstrap v5 is already available since May, 2021

Hi guys, @anon76427325 @ozfiddler thanks a lot for your reply but I’m still kind of stuck here.
So here’s what I would like to achieve:

1

I’m trying to create my own template inside of the quark theme in order to achieve this end effect.
So far what I’ve tried is to created a file called “teasermodul.yaml” inside of user/themes/quark/blueprints folder:

form:
  fields: 
    header.module_title:
      type: text
      size: large
      label: Page title
    header.teaser_list:
      type: list
      label: Teaser list
      min: 1
      max: 3
      fields:
        .img_link:
          label: Choose media
          type: pagemediaselect
        .teaser_title:
          type: text
          size: large
          label: Input title for teaser
        .teaser_text:
          type: text
          size: large
          label: Input text
        .readmore_link:
          type: text
          label: Input link
        .readmore_text:
          type: text
          label: Input description

And also a file called “teasermodul.html.twig” inside of user/themes/quark/templates folder:

<div class="container">
  <div class="columns">
    <div class="column col-4">
      <img />
      <h2></h2>
      <ul>
        <li></li>
      </ul>
    </div>
    <div class="column col-4">
      <img />
      <h2></h2>
      <ul>
        <li></li>
      </ul>
    </div>
    <div class="column col-4">
      <img />
      <h2></h2>
      <ul>
        <li></li>
      </ul>
    </div>
  </div>
</div>

I know they are not even close to correct, especially the html.twig file…
Just wondering if you have any suggestions ?

Thanks a lot!

@monin, Since this is an internship assignment, I’m not going to do your “homework”. But I will give you some pointers…

blueprint

  • You should extend the blueprint from an existing blueprint. See example A First Example
  • You blueprint definitions should be placed “inside” a tab field. See same example as above.
  • By using a new tab name, an new tab will be created for your Teaser fields.

Twig

  • The template should extend the base template which defines the <head>, content, footer etc.
    See for example /user/themes/quark/templates/default.html.twig
  • To access a value in the header of the page, use something like page.header.title
  • To print the title, use {{ page.header.title }}
  • Since the number of columns varies from 1-3, you will have to calculate the width of the columns and use that for each col-*:
    {% set columnWidth = 12/(page.header.teaser_list|count) %}
    
    {% for column in page.header.teaser_list %}
       <div class="column col-{{ columnWidth }}">
       ... 
     {% endfor %}
    
  • To create an image, you can use {{ page.media.images[column.img_link].html() | raw }}
  • To create a link <a href={{ column.readmore_link }}>{{ column.readmore_text }}</a>

I’ll leave the rest up to you to figure out…

This should be the end result if done correctly:
Untitled

2 Likes

Thanks a lot! I’ll give it a try!

Hey @anon76427325 , I have corrected my yaml file and html.twig file according to your suggestions but things are still not working correctly somehow. Can you please have a quick look here? Thanks!

Here’s my corrected yaml file:

title: Teaser
extends@:
    type: default
    context: blueprints://pages

form:
  fields:
    tabs:
      type: tabs
      active: 1

      fields:
        content:
          fields:
            header.teaser_list:
              type: list
              label: Teaser List
              min: 1
              max: 3

              fields:
                  .teaser_image:
                    type: pagemediaselect
                    folder: 'self@'
                    label: Select an image

                  .a_file:
                    type: filepicker
                    folder: 'self@'
                    preview_images: true
                    label: Select a file

                  .teaser_title:
                    type: text
                    label: Teaser Title

                  .teaser_text:
                    type: textarea
                    label: Teaser Text

                  .readmore_text:
                    type: text
                    label: Readmore Text

                  .readmore_link:
                    type: text
                    label: Readmore Link

I’m a little bit confused for image upload, I used both pagemediaselect and filepicker. Is this a redundancy? Should I only use one of them ?

And here’s my twig.html file:

{% extends 'partials/base.html.twig' %}

{% block content %}
    {{ page.content|raw }}
     
      {% set columnWidth = 12/(page.header.teaser_list|count) %}
      {% set columns = (page.header.teaser_list|count) %}
      {% for column in page.header.teaser_list %}
        <pre>
          {{ column|print_r }}
        </pre>
        <div class="column col-{{ columnWidth }}">
          {{ page.media[column.teaser_image].url }}
          {{ columm.teaser_title }}
          {{ column.teaser_text }}
          <a href={{ column.readmore_link }}>{{ column.readmore_text }}</a>
        </div>
      {% endfor %}
{% endblock %}

The teaser_title and teaser_image somehow won’t show up, and the columns are just piling up…

Any suggestions how i should modify it? Thanks!

pagemediaselect is for selecting already uploaded media item and filepicker is the file uploader itself IIRC. So it depends on what you actually want from the field - to upload files or to select from already uploaded.

1 Like

Hey @Karmalakas thanks for your reply. I just wanted to use the standard media upload and got confused

@monin, Well… that’s a start…

  • printing variables
    When printing a variable using {{ variable }}, it doesn’t create an element. It just dumps the value of the variable. You probably want to print the variable inside an element. See this primer on creating templates on the Twig site.
  • teaser_title
    If you make a typo in a field name, Twig will happily ignore the issue and print nothing.
  • teaser_image
    Have a look again at the code I’ve provided to create an image.
  • grid layout
    As pointed out before, have a look at how grid framework Spectre wants you to layout a grid using container, columns and column col-* and compare it with the layout you’ve created.
  • Don’t use {{ column|print_r }} to dump the content of a variable. Use {{ dump(variable) }} instead and inspect the value in the devtools of the browser. See docs.
  • Columns piling up
    Because you use <pre>, the printed content of a variable doesn’t wrap. This forces each column to be wider then col-4, which will force the next column to the next line.
1 Like

Thanks a lot! I’ll try it out!

Hey @anon76427325 , the page is finally working now!! I’m soo happy! Thanks again for your help!

1 Like

@monin, OK, nice, your initial technical question has been solved, but is it the best solution for the desired goal?

I’m just wondering… What are these teasers? Are they perhaps summaries of pages to which the ‘Read more’ links point?

In that case, the solution could be more elegant using:

  • a page collection
  • a listing page
  • page’s summary
  • page images stored in the folder of the page

See for example this demo of the Blog Skeleton, where each item in the list is a summary of a page. The title of the item points to the page itself. Instead of the masonry/bricklayer style of the list, picture yourself a 3-column display.

Yes, you are right. Thanks for your hint. I’ll keep working on it

@monin, I thought so…

Suggestion: Download Site Blog skeleton and take a look at:

  • the folder structure of /user/pages/01.blog
  • page /user/pages/01.blog/blog.md and its collection (= ‘content’ variable in frontmatter).
    This is the page that lists the blog items (your feature list)
  • each /user/pages/01.blog/*/item.md
    These are the blog items (your feature pages).
  • /user/themes/quark/templates/blog.html.twig. Especially lines 37-40 where the list of blog-items/features is created:
    {% for child in collection %}
        {% include 'partials/blog-list-item.html.twig' with {blog: page, page: child} %}
    {% endfor %}
    
  • /user/themes/quark/templates/partials/blog-list-item.html.twig which displays the feature summary in the list.

Happy reading, coding and learning!

2 Likes

Thanks!! I’ll try it out :blush: