Skip to content
Home » Blazor – My Portfolio – Part 4 – Styling Razor Components

Blazor – My Portfolio – Part 4 – Styling Razor Components

Spread the love

In the previous part of the series we created all the components for our app. They contain most of the elements they need, but they lack decent looks. Today we’ll fix that. We’re going to apply styles to our components and make them shine. We use CSS to add styles in Blazor, just like with other frontend frameworks. So, let’s talk about styling Razor components.

You can grab all the code and assets from Github.

Types of Component Styles 

There are multiple ways to add styles in Blazor. In particular, we have the following types of styles:

– Global styles – these are applied to the entire app,

– Scoped styles – these are applied only to specific components,

– Internal styles – these are defined inside the component’s template using the <style> tags,

– External styles – these are defined in a separate file,

– Inline styles – these are defined on particular elements using the style attribute.

Let’s have a look at some of these types one by one.

Global Styles 

Global styles apply to the entire application. You have access to them from any component in your app. They are defined in the wwwroot folder. In particular, you will see the app.css file there as well as the css folder with the bootstrap folder inside. Inside the bootstrap folder is the bootstrap.min.css file. It contains the minified version of CSS code, which is less human-readable, but more efficient for the machine.

You get Bootstrap styles out of the box when you create an app using the Blazor WebAssembly App template (but not the empty version of it). You don’t have to use Bootstrap styles in your project and create all your global styles in the app.css file. But many websites do make use of this library and we are going to use it too, pretty extensively.

If you want to know more about Bootstrap and all its classes, check out the documentation. The names of the classes are pretty self-explanatory, so you usually know what to expect from them.

As for the app.css file, it contains some classes already. We’re not going to add any new classes to it for now, until we start styling the sidebar components.

The styles we just discussed are referenced in the index.html file. Open the file and look at the <head> tag:

...

<head>
    ...
    <link href="css/bootstrap/bootstrap.min.css" rel="stylesheet" />
    <link href="css/app.css" rel="stylesheet" />
    ...
</head>

As you can see, the two CSS files are referenced inside the <link> tag.

Scoped Styles 

As your project grows, it gets more and more probable for name conflicts between CSS classes to appear. To solve this problem, we use scoped styles. Scoped styles apply only to the component in which they are defined. So, you can have the same class names for different components and they will not interfere with one another. We call this technique Blazor CSS isolation.

The Blazor CSS isolation occurs at build time. Unique attributes are used with each HTML tag behind the scenes and Blazor rewrites the CSS selectors to match the component’s markup with these attributes. This allows for a scoping effect. Then the rewritten CSS files are used as static assets in a file whose name consists of the app’s name + .styles.css. For example in our app the file is named MyPortfolio.styles.css. The file is referenced in the index.html file:

...
<head>
    ...
    <link href="MyPortfolio.styles.css" rel="stylesheet" />
</head>
...

How do we create scoped styles then? It’s easy. We just have to create a file in the same folder as the Razor component and use the component file’s name followed by .css. So, for example, our Project component is defined in the Project.razor file. So, the style must be defined in the Project.razor.css file in the same folder. The MainLayout and NavMenu components in the Shared folder come with their scoped files, which you can see over here:

We’ll be using scoped files a lot in our project. But first let’s have a look at some other styling options that we have.

Internal, External and Inline Styles 

We won’t be using internal, external or inline styles in our project, but they come in handy sometimes. If there is a small number of elements that you want to style and they are not used in other components, you can use one of these techniques.

Internal styles are styles defined in the component itself using the <style> tag. Let’s practice on our Project component and we’ll remove the styling when done. To style the outermost div, let’s assign it a class and then write CSS rules for this class inside a <styles> tag:

<div class="single-project">
    <img src="/images/Forest Monsters.png" />
    <div>
        <h5>Forest Monsters</h5>
        ...
        <p>Click to view more...</p>
    </div>
</div>

<style>
    .single-project
    {
        background-color: limegreen;
        text-align: end;
    }
</style>

@code {

}

If you run the app, you will see the change:

External styles are very similar, but the CSS code is moved to a separate file, which is then referenced by the component using a <link> tag. To avoid some caching issues, it’s best to reference external styles in the index.html file.

Inline styles are used directly on an HTML element using the style attribute. Let’s rewrite the Project component’s code using this technique:

<div style="background-color: limegreen; text-align: end">
    <img src="/images/Forest Monsters.png" />
    ...

As your style is now defined on the div element, don’t forget to remove the <style> tag. We don’t need a class for the div either.

After you’re done experimenting, remove the style attribute from the div element. We’re going to use Bootstrap and scoped styles for all our components.

Icons 

Our Project component contains a list of technologies and a list of links. At this moment both these lists contain plain text. They would be more eye-catching if instead of text we used icons. If you look at the sidebar, you will notice that the Home link is accompanied by an icon:

Where can we get the icons from? There are multiple sources you can tap into. But where does this Home icon come from? Well, it’s in the NavMenu component, which is in the Shared folder, so let’s open the file and try to spot it there:

<div class="top-row ps-3 navbar navbar-dark">
    ...
</div>

<div class="@NavMenuCssClass nav-scrollable" @onclick="ToggleNavMenu">
    <nav class="flex-column">
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="" Match="NavLinkMatch.All">
                <span class="oi oi-home" aria-hidden="true"></span> Home
            </NavLink>
        </div>
        ...
    </nav>
</div>

@code {
    ...
}

Here we have the <span> tag with the class set to “oi oi-home”. Change it to “oi oi-heart” and you will see a heart icon:

Now change it back. So, you can use special classes on an HTML element, like a <span> in the example above, to add an icon. These classes are defined in the open-iconic-bootstrap.min.css file.

The file is imported at the very top of the app.css file:

@import url('open-iconic/font/css/open-iconic-bootstrap.min.css');

This is all good, but, as you might have guessed, some icons we need may be missing there. Fortunately, we can use external resources as well. For our project component we’ll need icons to represent the particular technologies. We’ll find them on Devicon (https://devicon.dev/). We’ll also use Devicon icons for some of the links. For other links and for the categories we’ll use Bootstrap Icons (https://icons.getbootstrap.com/).

First of all, we have to reference them in index.html. If you visit their respective websites, you will find out how to do that:

...
<head>
    ...
    <link href="MyPortfolio.styles.css" rel="stylesheet" />
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/gh/devicons/devicon@v2.15.1/devicon.min.css">
    <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/bootstrap-icons@1.10.5/font/bootstrap-icons.css">
</head>
...

Now you can use the icons (both Devicon and Bootstrap Icons) using the <i> tag and setting the appropriate class. You can search for icons on their respective websites and there you will find out which class to use for each icon. Let’s see how it works on a real example. Let’s set the icons for the technologies and links. Open the Project.razor file and modify the code to contain icons instead of technology names:

<div>
    ...
        <div>
            Technologies:
            <div><i class="devicon-csharp-plain" /></div>
            <div><i class="devicon-unity-original" /></div>
        </div>
        ...
        <div>
            Links:
            <div><i class="devicon-github-original" /></div>
            <div><i class="bi bi-youtube" /></div>
            <div><i class="bi bi-book" /></div>
            <div><i class="bi bi-download" /></div>
        </div>
        ...

Run the app and watch the icons:

We’ll be using icons in several components in our app.

And now let’s style all our components one by one.

Styling the Project Component 

Let’s start by styling our Project component. Open the Project.razor file. If you now run your app, you will see multiple Project components displayed inside the ProjectsDisplay component. They don’t look very appealing.

Let’s add a scoped component that will apply only to this component. Right-click the Shared folder and select Add -> New Item… Then, under ASP.NET Core -> Web -> Content (A) select the Style Sheet template (B), name the file Project.razor.css (C) and hit the Add button (D).

The CSS file will appear under the Razor file.

Now let’s open the Project.razor file and add some styles. While you add the styles, Intellisense will display a Bootstrap icon if there is a matching Bootstrap class defined. For example there is a container class called card in Bootstrap. Here’s what you see while typing it in:

You won’t see the Bootstrap icon for all the styles, because some of them are not Bootstrap styles and will be defined in the Project.razor.css file. Anyway, make sure your code looks like this:

<div class="card">
    <img class="img-thumbnail" src="/images/Forest Monsters.png" />
    <div class="card-body">
        <h5 class="card-title mb-3">Forest Monsters</h5>
        <div class="tech-icons">            
            <div class="single-icon"><i class="devicon-csharp-plain"/></div>
            <div class="single-icon"><i class="devicon-unity-original"/></div>
        </div>
        <p>A 2D game made with Unity with C# scripting. Your task is to save the enchanted forest.</p>
        <div class="link-icons">
            <div class="single-icon"><i class="devicon-github-original" /></div>
            <div class="single-icon"><i class="bi bi-youtube" /></div>
            <div class="single-icon"><i class="bi bi-book" /></div>
            <div class="single-icon"><i class="bi bi-download" /></div>
        </div>
        <p class="more-info">Click to view more...</p>
    </div>
</div>

@code {

}

Now we have to define the styles. Some of the styles are Bootstrap styles, but we want to tweak them a bit. We can override some attributes inside our scoped style file. Scoped styles take precedence over global styles. Let’s take the card class as an example. Open the minified Bootstrap file (bootstrap.min.css), find the card class selector and copy its code. Don’t forget the dot before the selector name. This is how class selectors work in CSS.

Now paste the code in Project.razor.css. It will automatically get formatted in a more readable way:

.card {
    position: relative;
    display: flex;
    flex-direction: column;
    min-width: 0;
    word-wrap: break-word;
    background-color: #fff;
    background-clip: border-box;
    border: 1px solid rgba(0,0,0,.125);
    border-radius: .25rem
}

If you now add any attributes here, they will be merged with the attributes defined in Bootstrap. If you modify any existing attributes here, they will override the ones set in Bootstrap. Try to modify the background color for example:

.card {
    ...
    background-color: orange;
    ...
} 

Now run your app and the new background color should show up:

Also, the image is now very large. Don’t worry about that. We’ll fix it soon. And now let’s look at all the classes in our component and define the ones we need in the Project.razor.css file. We’ll also override some of the Bootstrap styles. If you run your app while making all these changes, you will see them live thanks to Hot Reload.

But before we start, let’s turn the component into a link so that if the user clicks on it, they will be taken to the details page. To do that, let’s just wrap the entire component in an anchor tag:

<a href="">
    <div class="card">
        ...
    </div>
</a>

@code {

}

Time for styling. Let’s start from the top. The card class is defined in Bootstrap, but we still want to override it. Add the following code in the Project.razor.css file:

.card {
    overflow: hidden;
    color: black;
    height: 520px;
    justify-content: space-between;
    margin-bottom: 10px;
}

Next, let’s take care of the image. We’ll use the <img> HTML element as the selector, so there is no dot before the name. Make sure your code looks like this:

.card {
    ...
}

img {
    height: 220px;
    width: 90%;
    object-fit: contain;
    align-self: center;
    margin-top: 10px;
    border: none;
}

Now the images are much smaller.

Next, let’s override the Bootstrap card-body class:

.card {
    ...
}

img {
    ...
}

.card-body {
    display: flex;
    flex-direction: column;
    justify-content: space-between;
}

We can leave the Bootstrap card-title class as is. Then we have the tech-icons and a bit further down the link-icons classes. These are our own classes, not from Bootstrap. We will style all the icons the same. We could use a common class for both the technologies and links, but who knows, maybe in the future we decide to style them differently. Anyway, add the following code:

.card {
    ...
}
...
.card-body {
    ...
}

.tech-icons,
.link-icons {
    display: flex;
    flex-wrap: wrap;
    justify-content: space-between;
    font-size: 25px;
}

We’re not going to define the single-icon class for now because we will do it later to animate the icons. Let’s define the more-info class:

.card {
    ...
}
...
.tech-icons,
.link-icons {
    ...
}

.more-info {
    font-size: 0.8em;
    margin-top: 1em;
}

At this point our Project component should look like this:

The text is underlined because it’s inside an anchor tag. Let’s fix it:

.card {
    ...
}
...
.more-info {
    ...
}

a {
    text-decoration: none;
}

Now it looks better:

We’re not yet done with the Project component because we’re going to add some hovering effects and animations, but let’s leave it for a little bit later. First, let’s style our ProjectsDisplay and Index components.

Styling the ProjectsDisplay and Index Components

We don’t need any scoped styles for the ProjectsDisplay component or for the Index component. Bootstrap styles will do. So, open the ProjectsDisplay.razor file and wrap each Project component with a div. Set the class of the div to col-md-6 col-lg-4 mb-2:

<div class="col-md-6 col-lg-4 mb-2">
    <Project />
</div>
<div class="col-md-6 col-lg-4 mb-2">
    <Project />
</div>
<div class="col-md-6 col-lg-4 mb-2">
    <Project />
</div>
<div class="col-md-6 col-lg-4 mb-2">
    <Project />
</div>
<div class="col-md-6 col-lg-4 mb-2">
    <Project />
</div>

@code {

}

These are actually three classes. What do we use them for?

The classes that start with col are used to create a responsive grid. The grid can span 1-12 grid columns. The md part stands for medium devices (desktops >= 992px). The lg part stands for large devices (desktops >= 1200px). The values 6 and 4 mean the component spans 6 or 4 grid columns out of 12 respectively, which gives us 12/6 = 2 columns or 12/4 columns respectively in the parent component.

So, the col-md-6 class will give us 2 columns on medium devices and the col-lg-4 class will give us 3 columns on large devices.

The mb-2 class is used to set the margin-bottom attribute to .5rem (check the documentation for more details).

To see it in action, let’s also modify the code in Index.razor by wrapping the ProjectsDisplay component in a div with the class row mt-3.

@page "/"

<PageTitle>Index</PageTitle>

<h1>My Projects</h1>

<div class="row mt-3">
    <ProjectsDisplay />
</div>

The row class is used as a container for responsive columns. The mt-3 class is used to set the margin-top attribute to 1rem.

Now you should see the Project components arranged in 3 columns on large devices:

If you resize the browser window below the value of a medium device, you will only see 2 columns:

As we didn’t specify the classes for small devices (tablets >= 768px) and extra small devices (phones < 768px), all 12 grid columns will be spanned, which will give us 12/12 = 1 column:

Now our components look pretty decent. As you remember, the Project components are links (we used the <a> tag for that), so if we click them, something should happen. We’ll take care of what is going to happen later in the series, but now let’s make the elements stand out visually when we hover over them. We’ll also add some animations.

Hovering Effects

To visually distinguish the project we’re hovering over from the ones we’re not, let’s add shadows to the Project component and animate the image to grow. As soon as we move the mouse out of a project, everything should go back to normal.

We use the :hover pseudo-class in CSS if we want to define the style for an element that is to be applied only while it’s hovered over.

So, go to the Project.razor.css file and add the following code:

...
a {
    ...
}

    a:hover .card {
        box-shadow: 0 4px 8px 0 rgba(0, 0, 0, 0.2), 0 6px 20px 0 rgba(0, 0, 0, 0.19);
        transition: box-shadow 0.3s ease-in-out;
    }

    a:hover img {
        transform: scale(1.07);
        transition: 0.5s ease-in-out;
    } 

The first selector, a:hover .card, will apply to elements with the class card that are inside an <a> element that is being hovered over. The second one, a:hover img, will apply to <img> elements inside <a> elements that are being hovered over.

In the code above we define two transitions. The first one will take care of setting the value of box-shadow. It will take 0.3 seconds and use a specific easing function to make the animation look smooth.

The second transition will change the scale of the image to 1.07 over 0.5 seconds, using the same easing function.

If you now hover your mouse over one of the projects, you will see both effects in action:

However, if your mouse moves outside the project, the effects will be reverted abruptly. To fix that, let’s add the same transitions to the elements when they are not hovered over, so without the :hover pseudoclass:

.card {
    transition: box-shadow 0.3s ease-in-out;
    overflow: hidden;
    ...
}

img {
    transition: 0.5s ease-in-out;
    height: 220px;
    ...
}
...

Now it looks much smoother. Let’s add some animations next.

Animations  

Let’s add a fancy little animation to the icons. Whenever you hover over an icon, it will rotate. We define an animation in CSS using the @keyframes rule. Let’s name our animation rotate-icon. We also have to specify the from a to values. Our icon is going to rotate around its axis and to make it a full circle, it must be from 0 to 360 degrees.

With the animation defined, all we have to do is apply it to our icon element (the one with the single-icon class) and set some attributes. We want the animation to be played only while an icon is hovered over. One rotation should take 1 second. We could set the animation to be played a specific number of times, but we want it to be looped infinitely. And the timing function should be linear so that the icon doesn’t accelerate or decelerate.

Add the following code to the ProjectsDisplay.razor.css file:

...
    a:hover img {
        ...
    }

@keyframes rotate-icon {
    from {
        transform: rotate(0deg)
    }

    to {
        transform: rotate(360deg);
    }
}

.single-icon:hover {
    animation-name: rotate-icon;
    animation-duration: 1s;
    animation-iteration-count: infinite;
    animation-timing-function: linear;
} 

Now run the app and hover your mouse over one of the icons. Watch it rotate.

The project summary is now styled. If the user clicks on it, they will navigate to the details page for that project. The navigation is not implemented yet, but you can go to this page by typing in /ProjectsDetails in the address bar. Here’s what you should see at this moment:

No, it doesn’t look great. Let’s fix it.

Styling the ProjectDetails Component

We’ll add scoped styles to the ProjectDetails component, so let’s create a ProjectDetails.razor.css file inside the Pages folder.

Then let’s add some classes to the component. Some of them are Bootstrap classes, some are our own. We’re also going to modify the code a little. Here it is in its new form:

@page "/ProjectDetails"

<h1 class="mb-5">Forest Monsters</h1>
<div class="row">
    <div class="col-lg-6 mb-4">
        <img class="img-fluid" src="/images/Forest Monsters.png" />
    </div>    
    <div class="col-lg-6">        
        <p>A 2D game made with Unity with C# scripting. Your task is to save the enchanted forest.</p>

        <h5 class="mb-4">Technologies used in this project:</h5>
        <div class="techs">
            <div class="tech">
                <span><i class="devicon-csharp-plain" />C#</span>  
            </div>
            <div class="tech">
                <span><i class="devicon-unity-original" />Unity</span>
            </div>
        </div>
        
        <h5 class="mb-4">Useful links related to this project:</h5>
        <div>
            <div class="link">
                <a href="https://github.com/prospero-apps/forest-monsters" target="_blank">
                    <span><i class="devicon-github-original" />Github</span>
                </a>
            </div>
            <div class="link">
                <a href="https://youtu.be/i7xyMbhdg_8" target="_blank">
                    <span><i class="bi bi-youtube" />YouTube</span>
                </a>
            </div>
            <div class="link">
                <a href="https://prosperocoder.com/posts/unity/forest-monsters-a-2d-platformer-game-made-with-unity" target="_blank">
                    <span><i class="bi bi-book" />My Prospero Coder blog</span>
                </a>
            </div>
            <div class="link">
                <a href="https://prosperocoder.itch.io/forest-monsters" target="_blank">
                    <span><i class="bi bi-download" />Download</span>
                </a>
            </div>
        </div>
    </div>
</div>


@code {

}

The Bootstrap classes should look familiar to you. As far as the scoped styles are concerned, make sure your CSS in the ProjectDetails.razor.css file matches the following:

.techs {
    margin-bottom: 20px;
}

.tech,
.link {
    font-size: 18px;
}

i {
    padding-right: 20px;
    width: 40px;
}

.link a {
    text-decoration: none;
}

    .link a:hover {
        font-weight: bolder;
    }

With these styles defined, our component should look like so:

Good. And now let’s finally do something about this horrible-looking sidebar.

Styling the Sidebar

There are three components in the sidebar (or inside the NavMenu component to be exact) that desperately need styling, CategoriesMenu, TechsMenu and SocialMenu. As for the last one of the three, it’ll be styled differently than the others. That’s why we’ll create scoped styles for it. As for the CategoriesMenu and TechsMenu components, though, they will be styled in a similar way, so to avoid code duplication, we’ll use global styles for them, so styles defined in the app.css file in the css subfolder of the wwwroot folder. If we decided to go with scoped styles instead, the two CSS files would have to be identical.

Let’s start from the top. We have to add classes to the tags in the CategoriesMenu component first. Here’s what the code should look like:

<div class="sidebar-menu">
    <h3 class="sidebar-header">Categories</h3>
    <div class="sidebar-scroll">
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="">
                <div class="sidebar-item">
                    <i class="bi bi-globe" />
                    <p>Web Projects</p>
                </div>
            </NavLink>
        </div>

        <div class="nav-item px-3">
            <NavLink class="nav-link" href="">
                <div class="sidebar-item">
                    <i class="bi bi-controller" />
                    <p>Games</p>
                </div>
            </NavLink>
        </div>

        <div class="nav-item px-3">
            <NavLink class="nav-link" href="">
                <div class="sidebar-item">
                    <i class="bi bi-box" />
                    <p>Blender Projects</p>
                </div>
            </NavLink>
        </div>

        <div class="nav-item px-3">
            <NavLink class="nav-link" href="">
                <div class="sidebar-item">
                    <i class="bi bi-book-half" />
                    <p>Books</p>
                </div>
            </NavLink>
        </div>

        <div class="nav-item px-3">
            <NavLink class="nav-link" href="">
                <div class="sidebar-item">
                    <i class="bi bi-easel" />
                    <p>Online Courses</p>
                </div>
            </NavLink>
        </div>

        <div class="nav-item px-3">
            <NavLink class="nav-link" href="">
                <div class="sidebar-item">
                    <i class=" bi bi-layout-text-window " />
                    <p>Magazines</p>
                </div>
            </NavLink>
        </div>
    </div>
</div>

@code {

}

As you can see, there are some Bootstrap styles as well. I also added more elements so that we can see how scrolling works. To this end I also wrapped the elements in a div for which scrolling will be enabled. The elements themselves are now NavLink components wrapped in div elements. I also added the icons.

The classes are defined in the app.css file. Let’s add a SIDEBAR section there to distinguish our sidebar classes from the rest. Here’s the code:

@import url('open-iconic/font/css/open-iconic-bootstrap.min.css');
...
    .loading-progress-text:after {
        ...
    }

/* SIDEBAR */
.sidebar-menu {
    margin: 5px;
    margin-bottom: 40px;
}

.sidebar-scroll {
    height: 200px;
    overflow-y: auto;
    padding-bottom: 10px;
    margin-left: -10px;
}

    .sidebar-scroll::-webkit-scrollbar {
        width: 10px;
        margin-right: 5px;
    }

    .sidebar-scroll::-webkit-scrollbar-thumb {
        background: linear-gradient(to bottom right, #008b8b 0%, #00008b 100%);
        border-radius: 5px;
    }

.sidebar-header {
    color: white;
    font-size: 1.2em;
    margin-top: 5px;
    text-align: center;
}

.nav-item a {
    border-radius: 40px;
    margin: 5px 0 5px 5px;
    padding: 2px 0 2px 10px;
}

    .nav-item a.active {
        background-color: rgba(255,255,255,0.25);
    }

    .nav-item a:hover {
        background-color: rgba(255,255,255,0.1);
    }

.sidebar-item {
    display: flex;
    align-content: center;
    color: white;
}

    .sidebar-item p {
        margin: 0;
    }

i {
    width: 30px;
    align-self: center;
}

If you now run the app, you will see that the top part of the sidebar looks way better. Also, try hovering your mouse over the particular elements to see the difference.

The same classes are going to be used in the TechsMenu component. Here’s the component’s temporary code:

<div class="sidebar-menu">
    <h3 class="sidebar-header">Technologies</h3>
    <div class="sidebar-scroll">
        <div class="nav-item px-3">
            <NavLink class="nav-link" href="">
                <div class="sidebar-item">
                    <i class="devicon-csharp-plain" />
                    <p>C#</p>
                </div>
            </NavLink>
        </div>

        <div class="nav-item px-3">
            <NavLink class="nav-link" href="">
                <div class="sidebar-item">
                    <i class="devicon-dotnetcore-plain" />
                    <p>.NET</p>
                </div>
            </NavLink>
        </div>

        <div class="nav-item px-3">
            <NavLink class="nav-link" href="">
                <div class="sidebar-item">
                    <i class="devicon-unity-original" />
                    <p>Unity</p>
                </div>
            </NavLink>
        </div>

        <div class="nav-item px-3">
            <NavLink class="nav-link" href="">
                <div class="sidebar-item">
                    <i class="devicon-html5-plain" />
                    <p>HTML</p>
                </div>
            </NavLink>
        </div>

        <div class="nav-item px-3">
            <NavLink class="nav-link" href="">
                <div class="sidebar-item">
                    <i class="devicon-css3-plain" />
                    <p>CSS</p>
                </div>
            </NavLink>
        </div>

        <div class="nav-item px-3">
            <NavLink class="nav-link" href="">
                <div class="sidebar-item">
                    <i class="devicon-javascript-plain" />
                    <p>JavaScript</p>
                </div>
            </NavLink>
        </div>

        <div class="nav-item px-3">
            <NavLink class="nav-link" href="">
                <div class="sidebar-item">
                    <i class="devicon-react-original" />
                    <p>React</p>
                </div>
            </NavLink>
        </div>

        <div class="nav-item px-3">
            <NavLink class="nav-link" href="">
                <div class="sidebar-item">
                    <i class="devicon-python-plain" />
                    <p>Python</p>
                </div>
            </NavLink>
        </div>

        <div class="nav-item px-3">
            <NavLink class="nav-link" href="">
                <div class="sidebar-item">
                    <i class="devicon-blender-original" />
                    <p>Blender</p>
                </div>
            </NavLink>
        </div>

        <div class="nav-item px-3">
            <NavLink class="nav-link" href="">
                <div class="sidebar-item">
                    <i class="devicon-azure-plain" />
                    <p>Azure</p>
                </div>
            </NavLink>
        </div>

        <div class="nav-item px-3">
            <NavLink class="nav-link" href="">
                <div class="sidebar-item">
                    <i class="devicon-microsoftsqlserver-plain" />
                    <p>MS SQL Server</p>
                </div>
            </NavLink>
        </div>

        <div class="nav-item px-3">
            <NavLink class="nav-link" href="">
                <div class="sidebar-item">
                    <i class="devicon-bootstrap-plain" />
                    <p>Bootstrap</p>
                </div>
            </NavLink>
        </div>

        <div class="nav-item px-3">
            <NavLink class="nav-link" href="">
                <div class="sidebar-item">
                    <i class="bi bi-people" />
                    <p>English</p>
                </div>
            </NavLink>
        </div>
    </div>
</div>

@code {

}

The technologies are displayed in a similar way as the categories. Let’s take care of the third component. SocialMenu will make use of some of the global styles, but it will also use its own scoped styles.

In the Shared folder create a SocialMenu.razor.css file. And now open the SocialMenu.razor file and modify the code to include some classes:

<div class="sidebar-social d-flex flex-column">
    <div class="sidebar-blogs">
        <h6 class="sidebar-header">My Blogs</h6>

        <div class="nav-item px-3 d-flex">
            <a href="https://prosperocoder.com/" target="_blank">
                <img class="img-fluid" src="images/SocialMedia/Logo Coder.png">
            </a>
            <a href="https://prosperoenglish.com/" target="_blank">
                <img class="img-fluid" src="images/SocialMedia/Logo English.png">
            </a>
        </div>
    </div>

    <div class="sidebar-youtube">
        <h6 class="sidebar-header">My YouTube Channels</h6>

        <div class="nav-item px-3 d-flex">
            <a href="https://www.youtube.com/c/ProsperoCoder/" target="_blank">
                <img class="img-fluid" src="images/SocialMedia/Prospero Coder YT Banner.png">
            </a>
            <a href="https://www.youtube.com/c/ProsperoBlender" target="_blank">
                <img class="img-fluid" src="images/SocialMedia/Prospero Blender YT Banner.png">
            </a>
            <a href="https://www.youtube.com/c/ProsperoEnglish" target="_blank">
                <img class="img-fluid" src="images/SocialMedia/Prospero English YT Banner.png">
            </a>
        </div>
    </div>


    <div class="sidebar-contact d-flex flex-column">
        <h6 class="sidebar-header">Contact</h6>
        <div class="sidebar-item">
            <i class="bi bi-envelope-at" />
            <p>prosperocoder@gmail.com</p>
        </div>
    </div>
</div>

In the contact section we added an envelope icon. Now, let’s define the component-specific styles in the SocialMenu.razor.css file:

.sidebar-social {
    align-items: center;
    color: white;
    font-size: 12px;
    margin-left: -10px;
    margin-top: -20px;
}

.sidebar-blogs,
.sidebar-yt,
.sidebar-contact {
    margin-top: 1.2em;
}

.sidebar-social img:hover {
    transform: scale(1.2);
    transition: 0.2s ease-in-out;
}

The scale of the images will be animated when you hover your mouse cursor over them. The sidebar with its three components now looks pretty decent:

If you resize the browser window, the entire sidebar will disappear when the window is too narrow. This is handled by the layout. Instead you will only see a navigation menu with three parallel bars:

If you click it, the sidebar will expand:

That’s the behavior you probably expect. Speaking of the layout… Let’s style it. It’s going to be visible all the time after all.

Styling MainLayout 

Pages are placed inside layouts. In our app there is just one layout, MainLayout, but there could be more. The default layout is set in the App.razor file. We use the same layout if the page is not found:

<Router AppAssembly="@typeof(App).Assembly">
    <Found Context="routeData">
        <RouteView RouteData="@routeData" DefaultLayout="@typeof(MainLayout)" />
        <FocusOnNavigate RouteData="@routeData" Selector="h1" />
    </Found>
    <NotFound>
        <PageTitle>Not found</PageTitle>
        <LayoutView Layout="@typeof(MainLayout)">
            <p role="alert">Sorry, there's nothing at this address.</p>
        </LayoutView>
    </NotFound>
</Router>

Let’s have a look at the MainLayout component then. What does it contain? Here’s the MainLayout.razor file:

@inherits LayoutComponentBase

<div class="page">
    <div class="sidebar">
        <NavMenu />
    </div>

    <main>
        <div class="top-row px-4">
            <a href="https://docs.microsoft.com/aspnet/" target="_blank">About</a>
        </div>

        <article class="content px-4">
            @Body
        </article>
    </main>
</div>

Here you can see the @inherits directive at the top, which is how inheritance is implemented in a Razor file. We’re going to have a look at this directive a bit later.

Next we have the NavMenu component, which is our sidebar. It’s assigned the class sidebar here. Below is the main tag that contains an About link and the @Body placeholder that will be replaced by the actual page.

There is also the MainLayout.razor.css file that contains the scoped styles for the layout. Here’s what you will find inside:

.page {
    position: relative;
    display: flex;
    flex-direction: column;
}

main {
    flex: 1;
}

.sidebar {
    background-image: linear-gradient(180deg, rgb(5, 39, 103) 0%, #3a0647 70%);
}

.top-row {
    background-color: #f7f7f7;
    border-bottom: 1px solid #d6d5d5;
    justify-content: flex-end;
    height: 3.5rem;
    display: flex;
    align-items: center;
}

    .top-row ::deep a, .top-row ::deep .btn-link {
        white-space: nowrap;
        margin-left: 1.5rem;
        text-decoration: none;
    }

    .top-row ::deep a:hover, .top-row ::deep .btn-link:hover {
        text-decoration: underline;
    }

    .top-row ::deep a:first-child {
        overflow: hidden;
        text-overflow: ellipsis;
    }

@media (max-width: 640.98px) {
    .top-row:not(.auth) {
        display: none;
    }

    .top-row.auth {
        justify-content: space-between;
    }

    .top-row ::deep a, .top-row ::deep .btn-link {
        margin-left: 0;
    }
}

@media (min-width: 641px) {
    .page {
        flex-direction: row;
    }

    .sidebar {
        width: 250px;
        height: 100vh;
        position: sticky;
        top: 0;
    }

    .top-row {
        position: sticky;
        top: 0;
        z-index: 1;
    }

    .top-row.auth ::deep a:first-child {
        flex: 1;
        text-align: right;
        width: 0;
    }

    .top-row, article {
        padding-left: 2rem !important;
        padding-right: 1.5rem !important;
    }
}

By the way, the @media sections are for devices with specified minimum or maximum widths. Anyway, if you run the application, you will see the three elements described above:

Let’s modify the layout slightly. The sidebar is fine, so let’s leave it as is. We don’t need the About link, so let’s cut it out. Then, we want an animated header with my photo (because this is My Portfolio) and some information about me for my potential employer. There’s also my Github link in the header. The code should now look like this:

<div class="page">
    <div class="sidebar">
        <NavMenu />
    </div>

    <main>
        <div class="top-row px-4 header">
            <div class="personal-info">
                <div class="personal-info-main">
                    <div class="personal-info-text">
                        <h2>Kamil Pakula - About me</h2>
                        <p>I'm looking for a job as an entry-level (junior / intern) frontend or mobile/desktop developer. As far as web development is concerned, I've had some experience with Blazor and this is my favorite framework. As far as mobile/desktop development is concerned, I use .NET MAUI. I'm sure I could learn other XAML-based technologies as well. I like learning new stuff. I like working with people, but I can also work individually.</p>
                        <p>Here are some of my projects...</p>
                    </div>
                    <div class="personal-info-image">
                        <img class='face-image' src='images/ID Photo.png' alt="face photo" />
                    </div>
                </div>
                <div class="personal-info-links">
                    <a href="https://github.com/prospero-apps" target="blank">
                        <i class="devicon-github-original github-icon"></i>&nbsp; Github
                    </a>
                </div>
            </div>
        </div>

        <article class="content px-4">
            @Body
        </article>
    </main>
</div>

For this to display correctly, you will need my photo. The image is available on Github, just copy it to the images folder in the wwwroot folder. As you can see, the elements are assigned different classes. Let’s define the appropriate styles in the MainLayout.razor.css file. To make them stand out a little more, let’s place them in a separate section, just above the @media blocks. I’ve also changed the colors in the linear gradient in the background of the sidebar. Here’s the modified MainLayout.razor.css file:

...
    .top-row ::deep a:first-child {
        ...
    }

/* CUSTOM STYLES */
.header {
    background-color: beige;
    height: 350px;
    display: flex;
    align-items: flex-end;
    justify-content: space-between;
    padding-top: 2em;
    box-shadow: 4px 4px 6px gray;
}

@keyframes rotate-personal-info {
    0% {
        transform: rotate(1.5deg) translateY(-2em);
    }

    50% {
        transform: rotate(-1.5deg) translateY(-2em);
    }

    100% {
        transform: rotate(1.5deg) translateY(-2em);
    }
}

.personal-info {
    background-color: white;
    margin: 1em;
    margin-top: 2em;
    padding: 0.5em;
    box-shadow: 2px 2px 3px gray;
    display: flex;
    flex-direction: column;
    transform: rotate(1.5deg) translateY(-2em);
    min-width: 500px;
    animation-name: rotate-personal-info;
    animation-duration: 10s;
    animation-iteration-count: infinite;
    animation-timing-function: ease-in-out;
}

    .personal-info:hover {
        animation-play-state: paused;
    }


.personal-info-main {
    display: flex;
}

.personal-info-image {
    align-self: flex-end;
}

.personal-info p,
.personal-info h2 {
    margin: 0.3em;
}

.personal-info-links a {
    text-decoration: none;
}

    .personal-info-links a:hover {
        text-decoration: none;
        font-weight: bolder;
    }

.face-image {
    width: 160px;
    padding: 20px;
}

.github-icon {
    color: black;
}

@media (max-width: 640.98px) {
    ...
    }
    ...

As you can see, there’s also an animation. It pauses when you hover your mouse above it. Go ahead and run the app. Here’s what it should look like:

Our app looks good, way better than before. However, everything is hard-coded in it. All projects are actually copies of just one project. Let’s fix that. In the next part of the series we’ll be talking about data and data binding.


Spread the love

Leave a Reply