Thursday 21 December 2023

Web Tutorial: The Random Christmas Card (Part 1/2)

It's less than a week to Christmas, and I'd like to welcome you to the traditional Christmas-themed web tutorial!

This year, just as we did back in 2017, we will be working with LESS. Nothing fancy, just that we will have multiple themes for the stylesheets and something like LESS makes sense. What we are attempting to create is a randomly generated online Christmas card with a random theme and a random message. This will be accomplished using PHP. With that in mind, let's proceed!

First, we have two folders - img and css. index.php will be in the main folder. styles.less will be in the css folder, with the LESS JavaScript file in the js folder. Three images will be in the img folder.

img/snow.jpg



img/food.jpg



img/nativity.jpg



We have some HTML here, with a reference to the LESS file and the JavaScript file for LESS.

index.php
<!DOCTYPE html>
<html>
    <head>
        <title>Xmas 2023</title>
        <link rel="stylesheet/less" type="text/css" href="css/styles.less" />

        <script src="js/less.min.js"></script>
    </head>

    <body>

    </body>
</html>


We will insert a placeholder for the body tag's class.

index.php
<body class="theme_">

</body>


Within, we will have a div styled using card.

index.php
<body class="theme_">
    <div class="card">

    </div>

</body>


And inside that, we have three more divs, one styled using image and the other styled using greeting. The last one is just to offset whatever the float properties for image and greeting eventually become.

index.php
<body class="theme_">
    <div class="card">
        <div class="image">

        </div>

        <div class="greeting">

        </div>

        <div style="clear: both"></div>

  </div>
</body>


And we'll have a header and paragraph tag inside the div styled by greeting.

index.php
<body class="theme_">
  <div class="card">
        <div class="image">

        </div>

        <div class="greeting">
          <h1></h1>
          <p></p>
        </div>

        <div style="clear: both"></div>
  </div>
</body>


The LESS file starts with some styling for the html and body tags. This basically just defines font size and resets margins and padding. We also define centered so that we can use this as a mixin for other classes later.

css/styles.less
html, body
{
    height: 100%;
    padding: 0px;
    margin: 0px;
    font-size: 14px;
}

.centered
{
    margin-left: auto;
    margin-right: auto;
}


There's not going to be much to see here. We first need to add some PHP. The first line we add is the variable name. If there is a parameter by the name of, well, name, in the URL, use that value. If not, just use the string "Human Being".

index.php
<?php
    $name = (isset($_GET["name"]) ? $_GET["name"] : "Human Being");
?>


<!DOCTYPE html>
<html>
    <head>
        <title>Xmas 2023</title>
        <link rel="stylesheet/less" type="text/css" href="css/styles.less" />

        <script src="js/less.min.js"></script>
    </head>

    <body>
        <div class="card">
            <div class="image">

            </div>

            <div class="greeting">
                <h1></h1>
                <p></p>
            </div>

            <div style="clear: both"></div>
        </div>
    </body>
</html>


Then we have an array, themes. It has three elements, each of them an array with keys. The name key values have been provided. The rest are right now empty strings.

index.php
<?php
    $name = (isset($_GET["name"]) ? $_GET["name"] : "Human Being");

    $themes = [
        ["name" => "snow", "title" => "", "prompt" => ""],
        ["name" => "food", "title" => "", "prompt" => ""],
        ["name" => "nativity", "title" => "", "prompt" => ""],
    ];

?>


Here, I've filled in the other values. title is what is going to appear as a header in your eventual output. These strings use the variable name. prompt is what we will be using later, to get content.

index.php
<?php
    $name = (isset($_GET["name"]) ? $_GET["name"] : "Human Being");

    $themes = [
        ["name" => "snow", "title" => "Seasons Greetings for a White Christmas, " . $name . "!", "prompt" => "Generate a short paragraph, between 80 to 100 words, as a greeting on a Christmas Card, involving snow and winter"],
        ["name" => "food", "title" => "Dear " . $name . ", wishing you festive food and fun!", "prompt" => "Generate a short poem, using less than 100 words, as a greeting on a Christmas Card, revoving around food"],
        ["name" => "nativity", "title" => "Have a blessed Christmas, " . $name . "!", "prompt" => "Generate a short paragraph, between 50 to 100 words, as a greeting on a Christmas Card, involving Jesus and the Nativity"]
    ];
?>


And then we use the rand() function to define the variable index as a value from 0 to 2. Then we define content as the prompt key value of the element of themes referenced by the value of index. Thus every time the page is reloaded, a different theme is used!

index.php
<?php
    $name = (isset($_GET["name"]) ? $_GET["name"] : "Human Being");

    $themes = [
        ["name" => "snow", "title" => "Seasons Greetings for a White Christmas, " . $name . "!", "prompt" => "Generate a short paragraph, between 80 to 100 words, as a greeting on a Christmas Card, involving snow and winter"],
        ["name" => "food", "title" => "Dear " . $name . ", wishing you festive food and fun!", "prompt" => "Generate a short poem, using less than 100 words, as a greeting on a Christmas Card, revoving around food"],
        ["name" => "nativity", "title" => "Have a blessed Christmas, " . $name . "!", "prompt" => "Generate a short paragraph, between 50 to 100 words, as a greeting on a Christmas Card, involving Jesus and the Nativity"]
    ];

    $index = rand(0, 2);

    $content = $themes[$index]["prompt"];

?>


However, since we're at the design stage, we hardcode the value of index at 0.

index.php
<?php
    $name = (isset($_GET["name"]) ? $_GET["name"] : "Human Being");

    $themes = [
        ["name" => "snow", "title" => "Seasons Greetings for a White Christmas, " . $name . "!", "prompt" => "Generate a short paragraph, between 80 to 100 words, as a greeting on a Christmas Card, involving snow and winter"],
        ["name" => "food", "title" => "Dear " . $name . ", wishing you festive food and fun!", "prompt" => "Generate a short poem, using less than 100 words, as a greeting on a Christmas Card, revoving around food"],
        ["name" => "nativity", "title" => "Have a blessed Christmas, " . $name . "!", "prompt" => "Generate a short paragraph, between 50 to 100 words, as a greeting on a Christmas Card, involving Jesus and the Nativity"]
    ];

    $index = rand(0, 2);
    $index = 0;

    $content = $themes[$index]["prompt"];
?>


In the HTML, add in the PHP code to display title strings and content.

index.php
<!DOCTYPE html>
<html>
    <head>
        <title>Xmas 2023</title>
        <link rel="stylesheet/less" type="text/css" href="css/styles.less" />

        <script src="js/less.min.js"></script>
    </head>

    <body class="theme_<?php echo $themes[$index]["name"]; ?>">
        <div class="card">
            <div class="image">

            </div>

            <div class="greeting">
                <h1><?php echo $themes[$index]["title"]; ?></h1>
                <p><?php echo $content; ?></p>
            </div>

            <div style="clear: both"></div>
        </div>
    </body>
</html>


If you were to run the code now, it would look like this. This is in dire need of styling.




We'll do it in the LESS file. Start by creating a style called theme_snow. That's the one that is currently active because we hardcoded the value of index to 0. While we're at it, create classes for theme_food and theme_nativity.

css/styles.less
html, body
{
    height: 100%;
    padding: 0px;
    margin: 0px;
    font-size: 14px;
}

.centered
{
    margin-left: auto;
    margin-right: auto;
}

.theme_snow
{

}

.theme_food
{

}

.theme_nativity
{

}



In here, we want to set the font and the background to a deep blue.

css/styles.less
.theme_snow
{
    font-family: arial;

    background: rgb(0, 0, 100);

}


Now we want to style card within theme_snow. In this case, card will be a translucent white rectangle in the middle of the screen, with rounded corners. We'll use centered here, which we defined earlier.

css/styles.less
.theme_snow
{
    font-family: arial;

    background: rgb(0, 0, 100);

    .card
    {
        margin-top: 10px;
        width: 600px;
        height: 650px;
        padding: 2em;
        border-radius: 10px;
        .centered;
        background: rgba(255, 255, 255, 0.5);
    }

}


We then style image. It will be circular and it will use the snowman image as its background. Again, it is in the middle of its parent, so use centered.

css/styles.less
.theme_snow
{
    font-family: arial;

    background: rgb(0, 0, 100);

    .card
    {
        margin-top: 10px;
        width: 600px;
        height: 650px;
        padding: 2em;
        border-radius: 10px;
        .centered;
        background: rgba(255, 255, 255, 0.5);
    }

    .image
    {
        width: 250px;
        height: 250px;
        border-radius: 50%;
        margin-bottom: 10px;
        background-image: url(../img/snow.jpg);
        background-position: center center;
        background-size: cover;
        background-repeat: no-repeat;
        .centered;
    }

}


Finally, we style greeting. It will have white text, and take the full width and a certain height.

css/styles.less
.theme_snow
{
    font-family: arial;

    background: rgb(0, 0, 100);

    .card
    {
        margin-top: 10px;
        width: 600px;
        height: 650px;
        padding: 2em;
        border-radius: 10px;
        .centered;
        background: rgba(255, 255, 255, 0.5);
    }

    .image
    {
        width: 250px;
        height: 250px;
        border-radius: 50%;
        margin-bottom: 10px;
        background-image: url(../img/snow.jpg);
        background-position: center center;
        background-size: cover;
        background-repeat: no-repeat;
        .centered;
    }

    .greeting
    {
        width: 100%;
        height: 350px;
        color: rgb(255, 255, 255);
    }

}


Refresh. Quite a drastic change, ain't it?




Back to the PHP, change the value of index.

index.php
$index = rand(0, 2);
$index = 1;


And now we will work on the CSS class theme_food. As before, we set the font and change the background color to a deep red.

css/styles.less
.theme_food
{
    font-family: verdana;

    background: rgb(150, 0, 0);

}


The card styling here is another rectangle, also centered using centered, and it's an opaque cream color. We use box-shadow to give this card a translucent black shadow.

css/styles.less
.theme_food
{
    font-family: verdana;

    background: rgb(150, 0, 0);

    .card
    {
        margin-top: 10px;
        width: 800px;
        height: 600px;
        padding: 1em;
        .centered;
        background: rgb(200, 200, 100);
        box-shadow: 10px 10px rgba(0, 0, 0, 0.5);
    }

}


For image, we are going to float it left and make it a tall rectangle. Then we are going to make food.jpg its background.

css/styles.less
.theme_food
{
    font-family: verdana;

    background: rgb(150, 0, 0);

    .card
    {
        margin-top: 10px;
        width: 800px;
        height: 600px;
        padding: 1em;
        .centered;
        background: rgb(200, 200, 100);
        box-shadow: 10px 10px rgba(0, 0, 0, 0.5);
    }

    .image
    {
        width: 50%;
        height: 100%;
        float: left;
        background-image: url(../img/food.jpg);
        background-position: center center;
        background-size: cover;
        background-repeat: no-repeat;
    }

}


For greeting, we will float it right.

css/styles.less
.theme_food
{
    font-family: verdana;

    background: rgb(150, 0, 0);

    .card
    {
        margin-top: 10px;
        width: 800px;
        height: 600px;
        padding: 1em;
        .centered;
        background: rgb(200, 200, 100);
        box-shadow: 10px 10px rgba(0, 0, 0, 0.5);
    }

    .image
    {
        width: 50%;
        height: 100%;
        float: left;
        background-image: url(../img/food.jpg);
        background-position: center center;
        background-size: cover;
        background-repeat: no-repeat;
    }

    .greeting
    {
        width: 45%;
        height: 100%;
        float: right;
    }

}


And the final result is...




Great. Time to work on the final theme, theme_nativity. This one is slightly different in the sense that image and greeting are not next to each other, but occupy the same space. They intersect.

First, change the value of index to 2.

index.php

$index = rand(0, 2);
$index = 2;


Define font and background for theme_nativity. Let' go for beige.
css/styles.less
.theme_nativity
{
    font-family: georgia;

    background: rgb(150, 100, 50);

}


card is going to be a large rectangle, with no defined background, and a thick brown border. Again, we use centered to center it - bet you're glad we're using LESS now!

css/styles.less
.theme_nativity
{
    font-family: georgia;

    background: rgb(150, 100, 50);

    .card
    {
        margin-top: 50px;
        width: 800px;
        height: 600px;
        .centered;
        border: 1em solid rgb(50, 0, 0);
    }

}


We will use image to fill up the whole of card. For this, we set the position property to absolute and use nativity.jpg as the background image.

css/styles.less

.theme_nativity
{
    font-family: georgia;

    background: rgb(150, 100, 50);

    .card
    {
        margin-top: 50px;
        width: 800px;
        height: 600px;
        .centered;
        border: 1em solid rgb(50, 0, 0);
    }

    .image
    {
        width: 800px;
        height: 600px;
        position: absolute;
        background-image: url(../img/nativity.jpg);
        background-position: center center;
        background-size: cover;
        background-repeat: no-repeat;
    }

}


Here, greeting will be a translucent black box with white text and rounded corners, set right in the middle of image. Again, position will be set to absolute for this to happen. The margin-left and margin-top properties are a function of the width and height of greeting compared to the width and height of image.

css/styles.less
.theme_nativity
{
    font-family: georgia;

    background: rgb(150, 100, 50);

    .card
    {
        margin-top: 50px;
        width: 800px;
        height: 600px;
        .centered;
        border: 1em solid rgb(50, 0, 0);
    }

    .image
    {
        width: 800px;
        height: 600px;
        position: absolute;
        background-image: url(../img/nativity.jpg);
        background-position: center center;
        background-size: cover;
        background-repeat: no-repeat;
    }

    .greeting
    {
        width: 400px;
        height: 300px;
        position: absolute;
        margin-left: 200px;
        margin-top: 150px;
        padding: 1em;
        background-color: rgba(0, 0, 0, 0.8);
        color: rgb(255, 255, 255);
        border-radius: 5px;
    }

}


We'll even add in some stying for h1 for shits and giggles. I want it to be in italics.

css/styles.less
.theme_nativity
{
    font-family: georgia;

    background: rgb(150, 100, 50);

    .card
    {
        margin-top: 50px;
        width: 800px;
        height: 600px;
        .centered;
        border: 1em solid rgb(50, 0, 0);
    }

    .image
    {
        width: 800px;
        height: 600px;
        position: absolute;
        background-image: url(../img/nativity.jpg);
        background-position: center center;
        background-size: cover;
        background-repeat: no-repeat;
    }

    h1
    {
        font-style: italic;
        font-size: 1.5em;
        text-align: center;
    }


    .greeting
    {
        width: 400px;
        height: 300px;
        position: absolute;
        margin-left: 200px;
        margin-top: 150px;
        padding: 1em;
        background-color: rgba(0, 0, 0, 0.8);
        color: rgb(255, 255, 255);
        border-radius: 5px;
    }
}


Neat!




This didn't take long at all. We've managed to create styles for three different themes with radically different layouts, through the power of LESS.

Next

Filling in the text using ChatGPT.

No comments:

Post a Comment