# Displaying projects in the portfolio

Let's work on displaying all our projects in the homepage.

For each project, we will display an image, the project title, and some of the technologies we used when developing it.

# The project data

Let's start by defining our projects. To do this, and to keep things simple, I'll just put them together with the Flask app code. You could conceivably store them in a database if you prefer:

projects = [
    {
        "name": "Habit tracking app with Python and MongoDB",
        "thumb": "img/habit-tracking.png",
        "hero": "img/habit-tracking-hero.png",
        "categories": ["python", "web"],
        "slug": "habit-tracking",
        "prod": "https://udemy.com",
    },
    {
        "name": "Personal finance tracking app with React",
        "thumb": "img/personal-finance.png",
        "hero": "img/personal-finance.png",
        "categories": ["react", "javascript"],
        "slug": "personal-finance",
    },
    {
        "name": "REST API Documentation with Postman and Swagger",
        "thumb": "img/rest-api-docs.png",
        "hero": "img/rest-api-docs.png",
        "categories": ["writing"],
        "slug": "api-docs",
    },
]

Each project has a few properties:

  • The name is what will be displayed on the homepage.
  • The thumb is the image shown on the homepage.
  • The hero is the image shown on each project page.
  • The categories are shown on the homepage.
  • The slug is the URL which is unique to each project (e.g. /project/habit-tracking would be the URL for the first project).

We also have to pass the projects to the homepage endpoint:

@app.route("/")
def home():
    return render_template("home.html", projects=projects)

# The HTML

Now that we've got the project data being passed to home.html, let's write the HTML.

{% extends 'base.html' %}
{% block content %}
    <main class="main main--home">
        <section class="projects">
            {% for project in projects %}
                <a class="u-bare-link" href="#">
                    <article class="project-card">
                        <img
                            class="project-card__image"
                            src="{{ url_for('static', filename=project['thumb']) }}"
                            alt="{{ project['name'] }} hero image"
                        />

                        <header class="project-card__meta">
                            <ul class="categories">
                                {% for category in project["categories"] %}
                                    <li class="categories__tag">{{ category}}</li>
                                {% endfor %}
                            </ul>

                            <h2 class="project-card__name" ">{{ project['name'] }}<h2/>
                        </header>
                    </article>
                </a>
            {% endfor %}
        </section>
    </main>
{% endblock %}

Using the content block, we define the main element as well as a section element for the projects.

Then, within a Jinja for loop, we define an a element for each project. The entire Project card (image, title, categories, and all the space in between) is clickable. That's why the a element encompasses everything else.

Inside the a element we've got an article for each project, which contains the thumbnail image inside an img element, and also a header element which contains the categories as a list and the name as an h2.

This doesn't look anything like what we want it to look, so let's get to CSS!

# The CSS

.projects {
  display: grid;
  grid-template-columns: 1fr;
  grid-column-gap: 1.25rem;
  justify-content: center;
  justify-items: center;
  max-width: 21.875rem;
  margin: 0 auto;
}

@media (min-width: 48.75em) {
  .projects {
    grid-template-columns: repeat(2, 1fr);
    max-width: 45rem;
  }
}

@media (min-width: 70em) {
  .projects {
    grid-template-columns: repeat(3, 1fr);
    max-width: 68rem;
  }
}

.project-card {
  padding: 0.75rem 0;
}

.project-card__image {
  max-width: 100%;
  margin-bottom: 1rem;
  transition: transform 0.2s ease-in-out;
}

.project-card__meta {
  padding: 0 1.25rem;
}

.project-card__name {
  margin: 0;
  font-size: 1.5rem;
  line-height: 2.25rem;
  color: #1c2023;
}

.project-card:hover .project-card__image {
  transform: translateY(-0.5rem);
}

.project-card:hover .project-card__name {
  text-decoration: underline;
}

.categories {
  display: flex;
  padding: 0;
  margin: 0 0 0.75rem;
  color: #3d84a3;
  list-style: none;
  line-height: 1.45;
  text-transform: uppercase;
  font-size: 1.1rem;
  letter-spacing: 0.075rem;
}

/* Adds an ::after pseudo-element to each category tag, except the last one.
   This psuedo-element contains a pipe character (|), which acts as a seperator
   between the tags. */

.categories__tag:not(:last-of-type)::after {
  content: "|";
  margin: 0 0.25rem;
}

/* Utility class to remove link styles */
.u-bare-link {
  text-decoration: none;
}

Here we're using CSS grid to build a 1 column grid to place projects. Then, when the screen width surpasses 48.75em, we change to 2 columns. After 70em we go to 3 columns.

For each screen width we also set the maximum width of our whole project grid. That means that the grid won't grow as the screen grows, but at certain breakpoints the number of columns will increase.

There are a couple new things introduced here.

# Transforms

.project-card:hover .project-card__image {
  transform: translateY(-0.5rem);
}

This transform[^css_transforms] moves the element by -0.5rem (so, upwards) when the parent .project-card element is hovered over.

# The ::after pseudo-element

Every HTML element can have ::before and ::after. These are pseudo-elements. That means we don't have to create them in the HTML code, but CSS can add them in for us.

Pseudo elements are added either immediately before or immediately after an element. Here we use it together with the content CSS property to add a | character after each element except the last one.

/* Adds an ::after pseudo-element to each category tag, except the last one.
   This psuedo-element contains a pipe character (|), which acts as a seperator
   between the tags. */
.categories__tag:not(:last-of-type)::after {
  content: "|";
  margin: 0 0.25rem;
}

# Utility classes

Sometimes a CSS utility class can be handy to apply certain properties that you don't want to duplicate in many Blocks or Elements. Here we use .u-bare-link (the u tells a reader this is a utility class) to remove link styles from the element it is applied to.

/* Utility class to remove link styles */
.u-bare-link {
  text-decoration: none;
}

# Linking to each project

You'll notice that the link to each project is, at the moment, #:

<a class="u-bare-link" href="#">

We need to replace that by a link to each project, naturally.

To do that we'll create a dynamic route and use url_for here.

We'll do that in the next lecture!