Layouts For Our Blade Components
In the previous article, we looked at breaking apart our header and storing it into components. This time, we’ll continue on and break apart our footer. We’ll then create a layout and add our body content in the next article
The Setup
In our components
directory, we’ll create a new subdirectory called layouts
. The layouts directory will contain a header
and a footer
subdirectory. The header directory will contain all of our code that we focused on in the previous article:
logo.blade.php
main.blade.php
nav.blade.php
nav-button.blade.php
nav-dropdown.blade.php
nav-dropdown-option.blade.php
The footer
subdirectory will contain our footer components that we’ll work on next.
We also need a layouts-test
directory and an index.blade.php
file. This is just like the previous article. This is where we’ll inject our header
and footer
into, for now.
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>Title</title>
<!-- Fonts -->
<link href="https://fonts.bunny.net/css2?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">
<!-- Styles -->
<style>
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}a{background-color:transparent}[hidden]{display:none}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{box-sizing:border-box;border:0 solid #e2e8f0}a{color:inherit;text-decoration:inherit}svg,video{display:block;vertical-align:middle}video{max-width:100%;height:auto}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity))}.border-t{border-top-width:1px}.flex{display:flex}.grid{display:grid}.hidden{display:none}.items-center{align-items:center}.justify-center{justify-content:center}.font-semibold{font-weight:600}.h-5{height:1.25rem}.h-8{height:2rem}.h-16{height:4rem}.text-sm{font-size:.875rem}.text-lg{font-size:1.125rem}.leading-7{line-height:1.75rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-1{margin-left:.25rem}.mt-2{margin-top:.5rem}.mr-2{margin-right:.5rem}.ml-2{margin-left:.5rem}.mt-4{margin-top:1rem}.ml-4{margin-left:1rem}.mt-8{margin-top:2rem}.ml-12{margin-left:3rem}.-mt-px{margin-top:-1px}.max-w-6xl{max-width:72rem}.min-h-screen{min-height:100vh}.overflow-hidden{overflow:hidden}.p-6{padding:1.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.pt-8{padding-top:2rem}.fixed{position:fixed}.relative{position:relative}.top-0{top:0}.right-0{right:0}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.text-center{text-align:center}.text-gray-200{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity))}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity))}.underline{text-decoration:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.w-5{width:1.25rem}.w-8{width:2rem}.w-auto{width:auto}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}@media (min-width:640px){.sm\:rounded-lg{border-radius:.5rem}.sm\:block{display:block}.sm\:items-center{align-items:center}.sm\:justify-start{justify-content:flex-start}.sm\:justify-between{justify-content:space-between}.sm\:h-20{height:5rem}.sm\:ml-0{margin-left:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pt-0{padding-top:0}.sm\:text-left{text-align:left}.sm\:text-right{text-align:right}}@media (min-width:768px){.md\:border-t-0{border-top-width:0}.md\:border-l{border-left-width:1px}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:1024px){.lg\:px-8{padding-left:2rem;padding-right:2rem}}@media (prefers-color-scheme:dark){.dark\:bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity))}.dark\:bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity))}.dark\:border-gray-700{--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity))}.dark\:text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.dark\:text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.dark\:text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}}
</style>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js" defer></script>
</head>
<body>
<x-layouts.header.main page-title="{{ $title }}" />
</body>
</html>
Finally, we need a route to get there.
<?php
Route::get('/component-layout', function() {
return view('layouts-test/index', [
'title' => 'Component Layout',
]);
});
Our Footer Component
The footer component, as we last saw it in Template Inheritance, is composed of a left column with logo and social media links, and four columns of regular links. How can we break this down far enough without overdoing it? I say that we stick to that outlined breakdown. Let’s not go crazy and incorporate a link component.
<footer>
<div class="max-w-screen-xl px-4 py-16 mx-auto sm:px-6 lg:px-8">
<div class="grid grid-cols-1 gap-8 lg:grid-cols-3">
<div>
<a href="#" class="text-lg font-semibold tracking-widest text-gray-900 uppercase rounded-lg dark-mode:text-white focus:outline-none focus:shadow-outline">Template Inheritance</a>
<p class="max-w-xs mt-4 text-sm text-gray-600">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptas, accusantium.
</p>
<div class="flex mt-8 space-x-6 text-gray-600">
<a class="hover:opacity-75" href target="_blank" rel="noreferrer">
<span class="sr-only"> Facebook </span>
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path fill-rule="evenodd" d="M22 12c0-5.523-4.477-10-10-10S2 6.477 2 12c0 4.991 3.657 9.128 8.438 9.878v-6.987h-2.54V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.988C18.343 21.128 22 16.991 22 12z" clipRule="evenodd" />
</svg>
</a>
<a class="hover:opacity-75" href target="_blank" rel="noreferrer">
<span class="sr-only"> Instagram </span>
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path fill-rule="evenodd" d="M12.315 2c2.43 0 2.784.013 3.808.06 1.064.049 1.791.218 2.427.465a4.902 4.902 0 011.772 1.153 4.902 4.902 0 011.153 1.772c.247.636.416 1.363.465 2.427.048 1.067.06 1.407.06 4.123v.08c0 2.643-.012 2.987-.06 4.043-.049 1.064-.218 1.791-.465 2.427a4.902 4.902 0 01-1.153 1.772 4.902 4.902 0 01-1.772 1.153c-.636.247-1.363.416-2.427.465-1.067.048-1.407.06-4.123.06h-.08c-2.643 0-2.987-.012-4.043-.06-1.064-.049-1.791-.218-2.427-.465a4.902 4.902 0 01-1.772-1.153 4.902 4.902 0 01-1.153-1.772c-.247-.636-.416-1.363-.465-2.427-.047-1.024-.06-1.379-.06-3.808v-.63c0-2.43.013-2.784.06-3.808.049-1.064.218-1.791.465-2.427a4.902 4.902 0 011.153-1.772A4.902 4.902 0 015.45 2.525c.636-.247 1.363-.416 2.427-.465C8.901 2.013 9.256 2 11.685 2h.63zm-.081 1.802h-.468c-2.456 0-2.784.011-3.807.058-.975.045-1.504.207-1.857.344-.467.182-.8.398-1.15.748-.35.35-.566.683-.748 1.15-.137.353-.3.882-.344 1.857-.047 1.023-.058 1.351-.058 3.807v.468c0 2.456.011 2.784.058 3.807.045.975.207 1.504.344 1.857.182.466.399.8.748 1.15.35.35.683.566 1.15.748.353.137.882.3 1.857.344 1.054.048 1.37.058 4.041.058h.08c2.597 0 2.917-.01 3.96-.058.976-.045 1.505-.207 1.858-.344.466-.182.8-.398 1.15-.748.35-.35.566-.683.748-1.15.137-.353.3-.882.344-1.857.048-1.055.058-1.37.058-4.041v-.08c0-2.597-.01-2.917-.058-3.96-.045-.976-.207-1.505-.344-1.858a3.097 3.097 0 00-.748-1.15 3.098 3.098 0 00-1.15-.748c-.353-.137-.882-.3-1.857-.344-1.023-.047-1.351-.058-3.807-.058zM12 6.865a5.135 5.135 0 110 10.27 5.135 5.135 0 010-10.27zm0 1.802a3.333 3.333 0 100 6.666 3.333 3.333 0 000-6.666zm5.338-3.205a1.2 1.2 0 110 2.4 1.2 1.2 0 010-2.4z" clipRule="evenodd" />
</svg>
</a>
<a class="hover:opacity-75" href target="_blank" rel="noreferrer">
<span class="sr-only"> Twitter </span>
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M8.29 20.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0022 5.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.072 4.072 0 012.8 9.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 012 18.407a11.616 11.616 0 006.29 1.84" />
</svg>
</a>
<a class="hover:opacity-75" href target="_blank" rel="noreferrer">
<span class="sr-only"> GitHub </span>
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clipRule="evenodd" />
</svg>
</a>
<a class="hover:opacity-75" href target="_blank" rel="noreferrer">
<span class="sr-only"> Dribbble </span>
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path fill-rule="evenodd" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10c5.51 0 10-4.48 10-10S17.51 2 12 2zm6.605 4.61a8.502 8.502 0 011.93 5.314c-.281-.054-3.101-.629-5.943-.271-.065-.141-.12-.293-.184-.445a25.416 25.416 0 00-.564-1.236c3.145-1.28 4.577-3.124 4.761-3.362zM12 3.475c2.17 0 4.154.813 5.662 2.148-.152.216-1.443 1.941-4.48 3.08-1.399-2.57-2.95-4.675-3.189-5A8.687 8.687 0 0112 3.475zm-3.633.803a53.896 53.896 0 013.167 4.935c-3.992 1.063-7.517 1.04-7.896 1.04a8.581 8.581 0 014.729-5.975zM3.453 12.01v-.26c.37.01 4.512.065 8.775-1.215.25.477.477.965.694 1.453-.109.033-.228.065-.336.098-4.404 1.42-6.747 5.303-6.942 5.629a8.522 8.522 0 01-2.19-5.705zM12 20.547a8.482 8.482 0 01-5.239-1.8c.152-.315 1.888-3.656 6.703-5.337.022-.01.033-.01.054-.022a35.318 35.318 0 011.823 6.475 8.4 8.4 0 01-3.341.684zm4.761-1.465c-.086-.52-.542-3.015-1.659-6.084 2.679-.423 5.022.271 5.314.369a8.468 8.468 0 01-3.655 5.715z" clipRule="evenodd" />
</svg>
</a>
</div>
</div>
<div class="grid grid-cols-1 gap-8 lg:col-span-2 sm:grid-cols-2 lg:grid-cols-4">
<div>
<p class="font-medium">
Company
</p>
<nav class="flex flex-col mt-4 space-y-2 text-sm text-gray-500">
<a class="hover:opacity-75" href> About </a>
<a class="hover:opacity-75" href> Meet the Team </a>
<a class="hover:opacity-75" href> History </a>
<a class="hover:opacity-75" href> Careers </a>
</nav>
</div>
<div>
<p class="font-medium">
Services
</p>
<nav class="flex flex-col mt-4 space-y-2 text-sm text-gray-500">
<a class="hover:opacity-75" href> Company Review </a>
<a class="hover:opacity-75" href> Accounts Review </a>
<a class="hover:opacity-75" href> HR Consulting </a>
</nav>
</div>
<div>
<p class="font-medium">
Helpful Links
</p>
<nav class="flex flex-col mt-4 space-y-2 text-sm text-gray-500">
<a class="hover:opacity-75" href> Contact </a>
<a class="hover:opacity-75" href> FAQs </a>
<a class="hover:opacity-75" href> Live Chat </a>
</nav>
</div>
<div>
<p class="font-medium">
Legal
</p>
<nav class="flex flex-col mt-4 space-y-2 text-sm text-gray-500">
<a class="hover:opacity-75" href> Privacy Policy </a>
<a class="hover:opacity-75" href> Terms & Conditions </a>
<a class="hover:opacity-75" href> Returns Policy </a>
<a class="hover:opacity-75" href> Accessibility </a>
</nav>
</div>
</div>
</div>
<p class="mt-8 text-xs text-gray-800">
© 2022 Comany Name
</p>
</div>
</footer>
Inside of our components/layouts/footer
directory, let’s create a main.blade.php
component. That will contain our code that’s shown above.
Next, let’s create a logo-column.blade.php
file for our logo and social media links.
<div>
<a href="#" class="text-lg font-semibold tracking-widest text-gray-900 uppercase rounded-lg dark-mode:text-white focus:outline-none focus:shadow-outline">Template Inheritance</a>
<p class="max-w-xs mt-4 text-sm text-gray-600">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptas, accusantium.
</p>
<div class="flex mt-8 space-x-6 text-gray-600">
<a class="hover:opacity-75" href target="_blank" rel="noreferrer">
<span class="sr-only"> Facebook </span>
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path fill-rule="evenodd" d="M22 12c0-5.523-4.477-10-10-10S2 6.477 2 12c0 4.991 3.657 9.128 8.438 9.878v-6.987h-2.54V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.988C18.343 21.128 22 16.991 22 12z" clipRule="evenodd" />
</svg>
</a>
<a class="hover:opacity-75" href target="_blank" rel="noreferrer">
<span class="sr-only"> Instagram </span>
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path fill-rule="evenodd" d="M12.315 2c2.43 0 2.784.013 3.808.06 1.064.049 1.791.218 2.427.465a4.902 4.902 0 011.772 1.153 4.902 4.902 0 011.153 1.772c.247.636.416 1.363.465 2.427.048 1.067.06 1.407.06 4.123v.08c0 2.643-.012 2.987-.06 4.043-.049 1.064-.218 1.791-.465 2.427a4.902 4.902 0 01-1.153 1.772 4.902 4.902 0 01-1.772 1.153c-.636.247-1.363.416-2.427.465-1.067.048-1.407.06-4.123.06h-.08c-2.643 0-2.987-.012-4.043-.06-1.064-.049-1.791-.218-2.427-.465a4.902 4.902 0 01-1.772-1.153 4.902 4.902 0 01-1.153-1.772c-.247-.636-.416-1.363-.465-2.427-.047-1.024-.06-1.379-.06-3.808v-.63c0-2.43.013-2.784.06-3.808.049-1.064.218-1.791.465-2.427a4.902 4.902 0 011.153-1.772A4.902 4.902 0 015.45 2.525c.636-.247 1.363-.416 2.427-.465C8.901 2.013 9.256 2 11.685 2h.63zm-.081 1.802h-.468c-2.456 0-2.784.011-3.807.058-.975.045-1.504.207-1.857.344-.467.182-.8.398-1.15.748-.35.35-.566.683-.748 1.15-.137.353-.3.882-.344 1.857-.047 1.023-.058 1.351-.058 3.807v.468c0 2.456.011 2.784.058 3.807.045.975.207 1.504.344 1.857.182.466.399.8.748 1.15.35.35.683.566 1.15.748.353.137.882.3 1.857.344 1.054.048 1.37.058 4.041.058h.08c2.597 0 2.917-.01 3.96-.058.976-.045 1.505-.207 1.858-.344.466-.182.8-.398 1.15-.748.35-.35.566-.683.748-1.15.137-.353.3-.882.344-1.857.048-1.055.058-1.37.058-4.041v-.08c0-2.597-.01-2.917-.058-3.96-.045-.976-.207-1.505-.344-1.858a3.097 3.097 0 00-.748-1.15 3.098 3.098 0 00-1.15-.748c-.353-.137-.882-.3-1.857-.344-1.023-.047-1.351-.058-3.807-.058zM12 6.865a5.135 5.135 0 110 10.27 5.135 5.135 0 010-10.27zm0 1.802a3.333 3.333 0 100 6.666 3.333 3.333 0 000-6.666zm5.338-3.205a1.2 1.2 0 110 2.4 1.2 1.2 0 010-2.4z" clipRule="evenodd" />
</svg>
</a>
<a class="hover:opacity-75" href target="_blank" rel="noreferrer">
<span class="sr-only"> Twitter </span>
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path d="M8.29 20.251c7.547 0 11.675-6.253 11.675-11.675 0-.178 0-.355-.012-.53A8.348 8.348 0 0022 5.92a8.19 8.19 0 01-2.357.646 4.118 4.118 0 001.804-2.27 8.224 8.224 0 01-2.605.996 4.107 4.107 0 00-6.993 3.743 11.65 11.65 0 01-8.457-4.287 4.106 4.106 0 001.27 5.477A4.072 4.072 0 012.8 9.713v.052a4.105 4.105 0 003.292 4.022 4.095 4.095 0 01-1.853.07 4.108 4.108 0 003.834 2.85A8.233 8.233 0 012 18.407a11.616 11.616 0 006.29 1.84" />
</svg>
</a>
<a class="hover:opacity-75" href target="_blank" rel="noreferrer">
<span class="sr-only"> GitHub </span>
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path fill-rule="evenodd" d="M12 2C6.477 2 2 6.484 2 12.017c0 4.425 2.865 8.18 6.839 9.504.5.092.682-.217.682-.483 0-.237-.008-.868-.013-1.703-2.782.605-3.369-1.343-3.369-1.343-.454-1.158-1.11-1.466-1.11-1.466-.908-.62.069-.608.069-.608 1.003.07 1.531 1.032 1.531 1.032.892 1.53 2.341 1.088 2.91.832.092-.647.35-1.088.636-1.338-2.22-.253-4.555-1.113-4.555-4.951 0-1.093.39-1.988 1.029-2.688-.103-.253-.446-1.272.098-2.65 0 0 .84-.27 2.75 1.026A9.564 9.564 0 0112 6.844c.85.004 1.705.115 2.504.337 1.909-1.296 2.747-1.027 2.747-1.027.546 1.379.202 2.398.1 2.651.64.7 1.028 1.595 1.028 2.688 0 3.848-2.339 4.695-4.566 4.943.359.309.678.92.678 1.855 0 1.338-.012 2.419-.012 2.747 0 .268.18.58.688.482A10.019 10.019 0 0022 12.017C22 6.484 17.522 2 12 2z" clipRule="evenodd" />
</svg>
</a>
<a class="hover:opacity-75" href target="_blank" rel="noreferrer">
<span class="sr-only"> Dribbble </span>
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path fill-rule="evenodd" d="M12 2C6.48 2 2 6.48 2 12s4.48 10 10 10c5.51 0 10-4.48 10-10S17.51 2 12 2zm6.605 4.61a8.502 8.502 0 011.93 5.314c-.281-.054-3.101-.629-5.943-.271-.065-.141-.12-.293-.184-.445a25.416 25.416 0 00-.564-1.236c3.145-1.28 4.577-3.124 4.761-3.362zM12 3.475c2.17 0 4.154.813 5.662 2.148-.152.216-1.443 1.941-4.48 3.08-1.399-2.57-2.95-4.675-3.189-5A8.687 8.687 0 0112 3.475zm-3.633.803a53.896 53.896 0 013.167 4.935c-3.992 1.063-7.517 1.04-7.896 1.04a8.581 8.581 0 014.729-5.975zM3.453 12.01v-.26c.37.01 4.512.065 8.775-1.215.25.477.477.965.694 1.453-.109.033-.228.065-.336.098-4.404 1.42-6.747 5.303-6.942 5.629a8.522 8.522 0 01-2.19-5.705zM12 20.547a8.482 8.482 0 01-5.239-1.8c.152-.315 1.888-3.656 6.703-5.337.022-.01.033-.01.054-.022a35.318 35.318 0 011.823 6.475 8.4 8.4 0 01-3.341.684zm4.761-1.465c-.086-.52-.542-3.015-1.659-6.084 2.679-.423 5.022.271 5.314.369a8.468 8.468 0 01-3.655 5.715z" clipRule="evenodd" />
</svg>
</a>
</div>
</div>
Let’s include it back in our footer/main
file.
<footer>
<div class="max-w-screen-xl px-4 py-16 mx-auto sm:px-6 lg:px-8">
<div class="grid grid-cols-1 gap-8 lg:grid-cols-3">
<x-layouts.footer.logo-column />
...
</div>
</footer>
For testing purposes, we also need to add our footer/main
component to our layouts-test/index
file. We should probably pass our page-title
there too.
<!DOCTYPE html>
<html lang="{{ str_replace('_', '-', app()->getLocale()) }}">
<head>
<meta charset="utf-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>{{ $title }}</title>
<!-- Fonts -->
<link href="https://fonts.bunny.net/css2?family=Nunito:wght@400;600;700&display=swap" rel="stylesheet">
<!-- Styles -->
<style>
/*! normalize.css v8.0.1 | MIT License | github.com/necolas/normalize.css */html{line-height:1.15;-webkit-text-size-adjust:100%}body{margin:0}a{background-color:transparent}[hidden]{display:none}html{font-family:system-ui,-apple-system,BlinkMacSystemFont,Segoe UI,Roboto,Helvetica Neue,Arial,Noto Sans,sans-serif,Apple Color Emoji,Segoe UI Emoji,Segoe UI Symbol,Noto Color Emoji;line-height:1.5}*,:after,:before{box-sizing:border-box;border:0 solid #e2e8f0}a{color:inherit;text-decoration:inherit}svg,video{display:block;vertical-align:middle}video{max-width:100%;height:auto}.bg-white{--tw-bg-opacity: 1;background-color:rgb(255 255 255 / var(--tw-bg-opacity))}.bg-gray-100{--tw-bg-opacity: 1;background-color:rgb(243 244 246 / var(--tw-bg-opacity))}.border-gray-200{--tw-border-opacity: 1;border-color:rgb(229 231 235 / var(--tw-border-opacity))}.border-t{border-top-width:1px}.flex{display:flex}.grid{display:grid}.hidden{display:none}.items-center{align-items:center}.justify-center{justify-content:center}.font-semibold{font-weight:600}.h-5{height:1.25rem}.h-8{height:2rem}.h-16{height:4rem}.text-sm{font-size:.875rem}.text-lg{font-size:1.125rem}.leading-7{line-height:1.75rem}.mx-auto{margin-left:auto;margin-right:auto}.ml-1{margin-left:.25rem}.mt-2{margin-top:.5rem}.mr-2{margin-right:.5rem}.ml-2{margin-left:.5rem}.mt-4{margin-top:1rem}.ml-4{margin-left:1rem}.mt-8{margin-top:2rem}.ml-12{margin-left:3rem}.-mt-px{margin-top:-1px}.max-w-6xl{max-width:72rem}.min-h-screen{min-height:100vh}.overflow-hidden{overflow:hidden}.p-6{padding:1.5rem}.py-4{padding-top:1rem;padding-bottom:1rem}.px-6{padding-left:1.5rem;padding-right:1.5rem}.pt-8{padding-top:2rem}.fixed{position:fixed}.relative{position:relative}.top-0{top:0}.right-0{right:0}.shadow{--tw-shadow: 0 1px 3px 0 rgb(0 0 0 / .1), 0 1px 2px -1px rgb(0 0 0 / .1);--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color), 0 1px 2px -1px var(--tw-shadow-color);box-shadow:var(--tw-ring-offset-shadow, 0 0 #0000),var(--tw-ring-shadow, 0 0 #0000),var(--tw-shadow)}.text-center{text-align:center}.text-gray-200{--tw-text-opacity: 1;color:rgb(229 231 235 / var(--tw-text-opacity))}.text-gray-300{--tw-text-opacity: 1;color:rgb(209 213 219 / var(--tw-text-opacity))}.text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}.text-gray-600{--tw-text-opacity: 1;color:rgb(75 85 99 / var(--tw-text-opacity))}.text-gray-700{--tw-text-opacity: 1;color:rgb(55 65 81 / var(--tw-text-opacity))}.text-gray-900{--tw-text-opacity: 1;color:rgb(17 24 39 / var(--tw-text-opacity))}.underline{text-decoration:underline}.antialiased{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.w-5{width:1.25rem}.w-8{width:2rem}.w-auto{width:auto}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}@media (min-width:640px){.sm\:rounded-lg{border-radius:.5rem}.sm\:block{display:block}.sm\:items-center{align-items:center}.sm\:justify-start{justify-content:flex-start}.sm\:justify-between{justify-content:space-between}.sm\:h-20{height:5rem}.sm\:ml-0{margin-left:0}.sm\:px-6{padding-left:1.5rem;padding-right:1.5rem}.sm\:pt-0{padding-top:0}.sm\:text-left{text-align:left}.sm\:text-right{text-align:right}}@media (min-width:768px){.md\:border-t-0{border-top-width:0}.md\:border-l{border-left-width:1px}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}}@media (min-width:1024px){.lg\:px-8{padding-left:2rem;padding-right:2rem}}@media (prefers-color-scheme:dark){.dark\:bg-gray-800{--tw-bg-opacity: 1;background-color:rgb(31 41 55 / var(--tw-bg-opacity))}.dark\:bg-gray-900{--tw-bg-opacity: 1;background-color:rgb(17 24 39 / var(--tw-bg-opacity))}.dark\:border-gray-700{--tw-border-opacity: 1;border-color:rgb(55 65 81 / var(--tw-border-opacity))}.dark\:text-white{--tw-text-opacity: 1;color:rgb(255 255 255 / var(--tw-text-opacity))}.dark\:text-gray-400{--tw-text-opacity: 1;color:rgb(156 163 175 / var(--tw-text-opacity))}.dark\:text-gray-500{--tw-text-opacity: 1;color:rgb(107 114 128 / var(--tw-text-opacity))}}
</style>
<script src="https://cdn.tailwindcss.com"></script>
<script src="https://cdn.jsdelivr.net/gh/alpinejs/alpine@v2.x.x/dist/alpine.min.js" defer></script>
</head>
<body>
<x-layouts.header.main page-title="{{ $title }}" />
<x-layouts.footer.main page-title="{{ $title }}" />
</body>
</html>
And then lets pass it to our left-column.blade.php
component from our footer/main
.
<footer>
<div class="max-w-screen-xl px-4 py-16 mx-auto sm:px-6 lg:px-8">
<div class="grid grid-cols-1 gap-8 lg:grid-cols-3">
<x-layouts.footer.logo-column page-title="{{ $attributes['page-title'] }}" />
...
</div>
</footer>
layouts/footer/logo-column
.<div>
<a href="#" class="text-lg font-semibold tracking-widest text-gray-900 uppercase rounded-lg dark-mode:text-white focus:outline-none focus:shadow-outline">
{{ $attributes['page-title'] }}
</a>
...
</div>
logo-column.blade.php
. It contains social media links that resemble each other.<a class="hover:opacity-75" href target="_blank" rel="noreferrer">
<span class="sr-only"> Facebook </span>
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path fill-rule="evenodd" d="M22 12c0-5.523-4.477-10-10-10S2 6.477 2 12c0 4.991 3.657 9.128 8.438 9.878v-6.987h-2.54V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.988C18.343 21.128 22 16.991 22 12z" clipRule="evenodd" />
</svg>
</a>
The only difference is the SVG file. I’m going to store all of those SVG files in a separate directory under components/layouts/svg
.
The Facebook
SVG looks like this:
<svg class="w-6 h-6" fill="currentColor" viewBox="0 0 24 24" aria-hidden="true">
<path fill-rule="evenodd" d="M22 12c0-5.523-4.477-10-10-10S2 6.477 2 12c0 4.991 3.657 9.128 8.438 9.878v-6.987h-2.54V12h2.54V9.797c0-2.506 1.492-3.89 3.777-3.89 1.094 0 2.238.195 2.238.195v2.46h-1.26c-1.243 0-1.63.771-1.63 1.562V12h2.773l-.443 2.89h-2.33v6.988C18.343 21.128 22 16.991 22 12z" clipRule="evenodd" />
</svg>
We now have 5 files:
dribbble.blade.php
facebook.blade.php
github.blade.php
instagram.blade.php
twitter.blade.php
We can reinsert those components back into our logo-column.blade.php
file.
<div>
<a href="#" class="text-lg font-semibold tracking-widest text-gray-900 uppercase rounded-lg dark-mode:text-white focus:outline-none focus:shadow-outline">
{{ $attributes['page-title'] }}
</a>
<p class="max-w-xs mt-4 text-sm text-gray-600">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptas, accusantium.
</p>
<div class="flex mt-8 space-x-6 text-gray-600">
<a class="hover:opacity-75" href target="_blank" rel="noreferrer">
<span class="sr-only"> Facebook </span>
<x-layouts.svg.facebook />
</a>
<a class="hover:opacity-75" href target="_blank" rel="noreferrer">
<span class="sr-only"> Instagram </span>
<x-layouts.svg.instagram />
</a>
<a class="hover:opacity-75" href target="_blank" rel="noreferrer">
<span class="sr-only"> Twitter </span>
<x-layouts.svg.twitter />
</a>
<a class="hover:opacity-75" href target="_blank" rel="noreferrer">
<span class="sr-only"> GitHub </span>
<x-layouts.svg.github />
</a>
<a class="hover:opacity-75" href target="_blank" rel="noreferrer">
<span class="sr-only"> Dribbble </span>
<x-layouts.svg.dribbble />
</a>
</div>
</div>
We now want to create a component for our social media links. For example:
<a class="hover:opacity-75" href target="_blank" rel="noreferrer">
<span class="sr-only"> Facebook </span>
<x-layouts.svg.facebook />
</a>
title
and a link
but how do we pass another component to it? We use what are called slots
. Let’s create a components/layouts/footer/social-media-link.blade.php
file first and add the code above. We’ll get it ready for receiving the content that we want to send to it.<a class="hover:opacity-75" href="{{ $attributes['link'] }}" target="_blank" rel="noreferrer">
<span class="sr-only"> {{ $attributes['title'] }} </span>
{{ $slot }}
</a>
The {{ $slot }}
is a reserved variable. It will put into it whatever we send in between the <x-layouts.footer.social-media-link> ... </x-layout.footer.social-media-link>
tags. So far, we’ve only looked at self-closing tags, but realistically, we can close the tag ourselves.
What do I mean by self-closing tags?
<x-layouts.footer.social-media-link />
How do you normally close any tag, like the <a>
tag? With a </a>
notation.
<x-layouts.footer.social-media-link>
...
</x-layout.footer.social-media-link>
Let’s see what our example actually looks like.
<x-layouts.footer.social-media-link link="#" title="Facebook">
Test
</x-layouts.footer.social-media-link>
Let’s refresh our page and see what the footer looks like.
Test
link shows up there. That means that we can simply pass the facebook
component there and it will show up where we intended it to show up.<div>
<a href="#" class="text-lg font-semibold tracking-widest text-gray-900 uppercase rounded-lg dark-mode:text-white focus:outline-none focus:shadow-outline">
{{ $attributes['page-title'] }}
</a>
<p class="max-w-xs mt-4 text-sm text-gray-600">
Lorem ipsum dolor sit amet consectetur adipisicing elit. Voluptas, accusantium.
</p>
<div class="flex mt-8 space-x-6 text-gray-600">
<x-layouts.footer.social-media-link link="#" title="Facebook">
<x-layouts.svg.facebook />
</x-layouts.footer.social-media-link>
<x-layouts.footer.social-media-link link="#" title="Instagram">
<x-layouts.svg.instagram />
</x-layouts.footer.social-media-link>
<x-layouts.footer.social-media-link link="#" title="Twitter">
<x-layouts.svg.twitter />
</x-layouts.footer.social-media-link>
<x-layouts.footer.social-media-link link="#" title="GitHub">
<x-layouts.svg.github />
</x-layouts.footer.social-media-link>
<x-layouts.footer.social-media-link link="#" title="Dribbble">
<x-layouts.svg.dribbble />
</x-layouts.footer.social-media-link>
</div>
</div>
If we refresh our page, we should notice that the Footer looks exactly like it did before.
What we just did with the {{ $slot }}
variable is a layout in a nutshell. We’ll revisit it in the next article, but that’s really all there is to it. Include a header, add a {{ $slot }}
, include the footer, and you’re done.
We’re not done with our footer yet though. We need to create a regular-column.blade.php
file that will store the code like the following.
<div>
<p class="font-medium">
Company
</p>
<nav class="flex flex-col mt-4 space-y-2 text-sm text-gray-500">
<a class="hover:opacity-75" href> About </a>
<a class="hover:opacity-75" href> Meet the Team </a>
<a class="hover:opacity-75" href> History </a>
<a class="hover:opacity-75" href> Careers </a>
</nav>
</div>
regular-column.blade.php
file actually look like? It contains a title and some links. That means we’ll need a loop to display those links since we’ll probably have to pass an array of data to it. Passing arrays is slightly different but not something that we can’t do. First, our regular-column
component.<div>
<p class="font-medium">
{{ $attributes['column-title'] }}
</p>
<nav class="flex flex-col mt-4 space-y-2 text-sm text-gray-500">
@foreach($attributes['links'] as $link => $link_title)
<a class="hover:opacity-75" href="{{ $link }}"> {{ $link_title }} </a>
@endforeach
</nav>
</div>
As you can see, there’s a foreach loop that will loop through each of our links. Our array will be an associative array with key/value pairs.
Calling our component is straightforward. It’s the same as what we’ve been doing so far.
<x-layouts.footer.regular-column
column-title="Company"
:links="['/about' => 'About', '/meet-the-team' => 'Meet the Team', '/history' => 'History', '/careers' => 'Careers' ]"
/>
Look at the two different attributes that we’re passing: column-title
and :links
. According to the Laravel documentation, “hard-coded, primitive values may be passed to the component using simple HTML attribute strings. PHP expressions and variables should be passed to the component via attributes that use the : character as a prefix.”
Since we’re passing an array through our links
attribute, we need to prefix it with a colon, i.e. :links
. We can now modify the rest of the columns to do the same in our components/layouts/footer/main
file.
<footer>
<div class="max-w-screen-xl px-4 py-16 mx-auto sm:px-6 lg:px-8">
<div class="grid grid-cols-1 gap-8 lg:grid-cols-3">
<x-layouts.footer.logo-column page-title="{{ $attributes['page-title'] }}" />
<div class="grid grid-cols-1 gap-8 lg:col-span-2 sm:grid-cols-2 lg:grid-cols-4">
<x-layouts.footer.regular-column
column-title="Company"
:links="['/about' => 'About', '/meet-the-team' => 'Meet the Team', '/history' => 'History', '/careers' => 'Careers' ]"
/>
<x-layouts.footer.regular-column
column-title="Services"
:links="['/company-review' => 'Company Review', '/accounts-review' => 'Accounts Review', '/hr-consulting' => 'HR Consulting' ]"
/>
<x-layouts.footer.regular-column
column-title="Helpful Links"
:links="['/contact' => 'Contact', '/faqs' => 'FAQs', '/live-chat' => 'Live Chat' ]"
/>
<x-layouts.footer.regular-column
column-title="Legal"
:links="['/privacy' => 'Privacy Policy', '/faqs' => 'FAQs', 'terms' => 'Terms & Conditions', '/returns' => 'Returns Policy', 'accessibility' => 'Accessibility' ]"
/>
</div>
</div>
<p class="mt-8 text-xs text-gray-800">
© 2023 Comany Name
</p>
</div>
</footer>
In case you wanted to see the folder structure, here it is once more.
In the next article, we’ll complete our layout and create a couple of pages.
Laravel Series
Continue your Laravel Learning.
Component Inception
Laravel – P17: Components Within Components
We’re going to see what the true power of components looks like; I should have called this article Component Inception since we’ll be going deep inside our components.
Layouts For Our Blade Components
Laravel – P18: Component Layout Prep-Work
How do we create a layout that we can reuse with our Blade Components? That’s what this article is going to answer with the entire layout: header, footer, etc.
Actually Creating the Layout
Laravel – P19: Component Layouts
Believe it or not, the difficult part was covered in the previous article. Breaking apart the header and footer components was a tedious task. Creating a layout is simple.