Currently, we have a skeleton.blade.php
layout file that we extend in all our application views. Since we will have different types of views in our app, it would be nice to have different templates to extend depending on the view we are creating. As our application views will have different sections compared to other views, we’ll create a aplication.blade.php
file within our layouts
folder.
--- /dev/null
+++ b/resources/views/layouts/application.blade.php
@@ -0,0 +1,27 @@
+@extends('layouts.skeleton')
+
+@section('pageTitle', 'PingPing')
+@section('bodyClasses', 'bg-grey-lighter text-base text-grey-darkest font-normal relative')
+
+@section('body')
+ <div class="min-h-screen flex flex-col">
+ @include('layouts.application.partials.navigation')
+
+ <main class="flex-1">
+ <div class="w-full max-w-screen mx-auto px-4 flex py-10">
+ @hasSection('sidebar')
+ <nav class="hidden lg:block lg:w-1/6 mr-6">
+ @yield('sidebar')
+ </nav>
+ @endif
+
+ <div class="flex-1">
+ @yield('content')
+ </div>
+ </div>
+ </main>
+
+ @include('layouts.application.partials.footer')
+ </div>
+ @
+@stop
Here we are extending our skeleton.blade.php
file and including a few partials for the navigation, sidebar and footer. We’ll create those in a second.
We already have a services.blade.php
file that was extending our skeleton.blade.php
layout, let’s update that now and we’ll also add some styling to that page.
--- a/resources/views/services/index.blade.php
+++ b/resources/views/services/index.blade.php
@@ -1,14 +1,26 @@
-<h1>Services</h1>
+@extends('layouts.application')
-<table>
- <tr>
- <td>Alias</td>
- <td>URL</td>
- </tr>
- @foreach ($services as $service)
- <tr>
- <td>{{ $service->alias }}</td>
- <td>{{ $service->url }}</td>
- </tr>
- @endforeach
-</table>
+@section('content')
+ <div class="bg-white rounded shadow border-t-4 border-primary">
+ <div class="flex justify-between items-center px-8 py-4 border-b">
+ <div class="text-lg text-black uppercase font-bold tracking-widest">
+ Services
+ </div>
+ </div>
+
+ <div class="px-8 py-8 bg-grey-lightest rounded-b">
+ <table>
+ <tr>
+ <td>Alias</td>
+ <td>URL</td>
+ </tr>
+ @foreach ($services as $service)
+ <tr>
+ <td>{{ $service->alias }}</td>
+ <td>{{ $service->url }}</td>
+ </tr>
+ @endforeach
+ </table>
+ </div>
+ </div>
+@stop
We will be using a package called thomaswelton/laravel-gravatar
. Install it with Composer by running composer require "thomaswelton/laravel-gravatar"
. This package enables us to fetch an avatar for the logged in user. With this, we don't need to worry about allowing our users to upload an avatar to our application, we'll simply pull their avatar from gravatar.com.
In the layout file we creating above, we included a few partials for the navigation, sidebar and footer sections of our app. Let’s create them now. Separating our views into smaller parts is an easy way to keep things organized.
--- /dev/null
+++ b/resources/views/layouts/application/partials/navigation.blade.php
@@ -0,0 +1,43 @@
+<nav class="w-full max-w-screen mx-auto px-4">
+ <div class="flex mt-6 py-3 px-2 border-b border-grey">
+ <div class="hidden lg:block lg:w-1/6 mr-6">
+ <a href="{{ route('services') }}">
+ @include('layouts.application.logo', ['class' => 'w-32'])
+ </a>
+ </div>
+ <div class="flex flex-auto justify-between items-center">
+ <ul class="list-reset flex pr-4">
+ <li>
+ <a href="{{ route('services') }}" class="flex items-center no-underline text-grey-darker group hover:text-black mr-10">
+ @include('assets.icons.services', ['class' => 'w-6 h-6 text-primary mr-2 group-hover:text-primary'])
+ <span class="font-bold text-black">Services</span>
+ </a>
+ </li>
+ <li>
+ <a href="{{ route('settings.profile.edit') }}" class="flex items-center no-underline text-grey-darker group hover:text-black mr-10">
+ @include('assets.icons.cog', ['class' => 'w-6 h-6 text-grey-dark mr-2 group-hover:text-primary'])
+ <span>Settings</span>
+ </a>
+ </li>
+ <li>
+ <a href="{{ route('billing.credits') }}" class="flex items-center no-underline text-grey-darker group hover:text-black mr-10">
+ @include('assets.icons.billing', ['class' => 'w-6 h-6 text-grey-dark mr-2 group-hover:text-primary'])
+ <span>Billing</span>
+ </a>
+ </li>
+ </ul>
+
+ <div class="flex items-center">
+ <span class="text-primary font-bold border-r border-grey pr-4 mr-4">100 Credits</span>
+ <ul class="list-reset flex pr-4">
+ <li>
+ <a href="{{ route('logout') }}" class="flex items-center no-underline text-grey-darker group hover:text-black">
+ <img src="{{ Gravatar::src(auth()->user()->email, 200) }}" class="w-6 h-6 rounded-full mr-2"/>
+ <span>Logout</span>
+ </a>
+ </li>
+ </ul>
+ </div>
+ </div>
+ </div>
+</nav>
--- /dev/null
+++ b/resources/views/settings/partials/sidebar.blade.php
@@ -0,0 +1,28 @@
+<ul class="list-reset mb-8">
+ <div class="pb-3 font-bold uppercase text-black">Settings</div>
+
+ <li>
+ <a href="#" class="flex items-center no-underline text-grey-darker group hover:text-black py-2">
+ @include('assets.icons.profile', ['class' => 'w-6 h-6 text-primary mr-3 group-hover:text-primary'])
+ <span class="font-bold text-black">My Profile</span>
+ </a>
+ </li>
+ <li>
+ <a href="#" class="flex items-center no-underline text-grey-darker group hover:text-black py-2">
+ @include('assets.icons.security', ['class' => 'w-6 h-6 text-grey-dark mr-3 group-hover:text-primary'])
+ <span class="">Security</span>
+ </a>
+ </li>
+ <li>
+ <a href="#" class="flex items-center no-underline text-grey-darker group hover:text-black py-2">
+ @include('assets.icons.notifications', ['class' => 'w-6 h-6 text-grey-dark mr-3 group-hover:text-primary'])
+ <span class="">Notifications</span>
+ </a>
+ </li>
+ <li>
+ <a href="#" class="flex items-center no-underline text-grey-darker group hover:text-black py-2">
+ @include('assets.icons.api', ['class' => 'w-6 h-6 text-grey-dark mr-3 group-hover:text-primary'])
+ <span class="">API Access</span>
+ </a>
+ </li>
+</ul>
--- /dev/null
+++ b/resources/views/layouts/application/partials/footer.blade.php
@@ -0,0 +1,12 @@
+<footer>
+ <div class="w-full max-w-screen mx-auto px-4 py-8 text-sm text-secondary">
+ <div class="flex justify-between">
+ <div>
+ © 2017-{{ date('Y') }}. Created by <a href="https://twitter.com/stefanbauerme" target="_blank" rel="noopener noreferer" class="no-underline text-primary hover:text-
primary-dark">@stefanbauerme</a>
+ </div>
+ <div>
+ Some more links...
+ </div>
+ </div>
+ </div>
+</footer>
I’ve also created an icons
folder within the partials
folder. Inside there I have placed some files that contain some SVG icons we’ll just include where necessary using the @include
blade directive. Take a look at the commit for episode #007: Designing a basic application layout to see all the icons we created.
We only have a handful of endpoints defined for our application. It’s time to add a few more. We need to create two routes, one for our credits resource and on for our profile page.
--- a/routes/web.php
+++ b/routes/web.php
@@ -23,5 +23,7 @@ Route::get('/logout', 'Auth\LoginController@logout')->name('logout');
Route::middleware(['auth'])->group(function () {
Route::get('/services', 'ServicesController@index')->name('services');
+ Route::get('/settings/profile', 'ProfileController@edit')->name('settings.profile.edit');
+ Route::get('/billing', 'CreditsController@index')->name('billing.credits');
});
We created our routes within the group that has the auth
middleware applied to it, anyone who visits those routes must be logged in our application. We’ll create the controllers in a bit.
We need to create the controllers that we defined in our routes file. We will create two controllers called CreditsController
and ProfileController
. The CreditsController
will be used to handle everything related to our credits resource. In the other hand, the ProfileController
will be used for everything related to the users' profile. At the moment we are only returning a view from each controller.
--- /dev/null
+++ b/app/Http/Controllers/CreditsController.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace App\Http\Controllers;
+
+class CreditsController extends Controller
+{
+ public function index()
+ {
+ return view('billing.credits.index');
+ }
+}
--- /dev/null
+++ b/app/Http/Controllers/ProfileController.php
@@ -0,0 +1,11 @@
+<?php
+
+namespace App\Http\Controllers;
+
+class ProfileController extends Controller
+{
+ public function edit()
+ {
+ return view('settings.profile.edit');
+ }
+}
Here we created our 2 controllers, they each have one method that simply renders a corresponding view.
We previously created two controllers that each render a view. We are going to create those views now. The views will be very simple for now, but we'll keep adding new things as we move forward.
--- /dev/null
+++ b/resources/views/billing/credits/index.blade.php
@@ -0,0 +1,15 @@
+@extends('layouts.application')
+
+@section('content')
+ <div class="bg-white rounded shadow border-t-4 border-primary">
+ <div class="flex justify-between items-center px-8 py-4 border-b">
+ <div class="text-lg text-black uppercase font-bold tracking-widest">
+ Credits
+ </div>
+ </div>
+
+ <div class="px-8 py-8 bg-grey-lightest rounded-b">
+ ...
+ </div>
+ </div>
+@stop
--- /dev/null
+++ b/resources/views/settings/profile/edit.blade.php
@@ -0,0 +1,19 @@
+@extends('layouts.application')
+
+@section('sidebar')
+ @include ('settings.partials.sidebar')
+@stop
+
+@section('content')
+ <div class="bg-white rounded shadow border-t-4 border-primary">
+ <div class="flex justify-between items-center px-8 py-4 border-b">
+ <div class="text-lg text-black uppercase font-bold tracking-widest">
+ My Profile
+ </div>
+ </div>
+
+ <div class="px-8 py-8 bg-grey-lightest rounded-b">
+ ...
+ </div>
+ </div>
+@stop
We’ve been working mostly with the default configuration that Tailwind provides, but It would be nice to have a similar color palette like bootstrap and be able to use classes like success
, danger
, primary
, etc. When using Tailwind, adding colors to use with utilities is very simple.
--- a/tailwind.js
+++ b/tailwind.js
@@ -56,10 +56,29 @@ let colors = {
'grey-lightest': '#fcfcfc',
'white': '#ffffff',
- 'primary': '#2b79c1',
- 'primary-dark': '#266299',
+ 'primary-light': '#93c7fe',
+ 'primary': '#4598db',
+ 'primary-dark': '#006cb8',
- 'red': '#bf6464',
+ 'secondary-light': '#d2d5da',
+ 'secondary': '#96a5b8',
+ 'secondary-dark': '#5a7897',
+
+ 'info-light': '#a9f8ff',
+ 'info': '#4cc8f9',
+ 'info-dark': '#009acc',
+
+ 'warning-light': '#fffa9a',
+ 'warning': '#ecc657',
+ 'warning-dark': '#b99600',
+
+ 'success-light': '#a2ffa9',
+ 'success': '#5ed66e',
+ 'success-dark': '#00a633',
+
+ 'danger-light': '#ff8795',
+ 'danger': '#ed4763',
+ 'danger-dark': '#bf0036'
}
module.exports = {
@@ -294,6 +313,24 @@ module.exports = {
backgroundColors: colors,
+ /*
+ |-----------------------------------------------------------------------------
+ | Background sizes https://tailwindcss.com/docs/background-size
+ |-----------------------------------------------------------------------------
+ |
+ | Here is where you define your background sizes. We provide some common
+ | values that are useful in most projects, but feel free to add other sizes
+ | that are specific to your project here as well.
+ |
+ | Class name: .bg-{size}
+ |
+ */
+
+ backgroundSize: {
+ 'auto': 'auto',
+ 'cover': 'cover',
+ 'contain': 'contain',
+ },
/*
|-----------------------------------------------------------------------------
@@ -517,6 +554,7 @@ module.exports = {
'4xl': '90rem',
'5xl': '100rem',
'full': '100%',
+ 'screen': '1400px',
},
@@ -593,6 +631,7 @@ module.exports = {
'4': '1rem',
'6': '1.5rem',
'8': '2rem',
+ '10': '2.5rem',
},
@@ -740,7 +779,12 @@ module.exports = {
| Here is where you control which modules are generated and what variants are
| generated for each of those modules.
|
|
| To disable a module completely, use `false` instead of an array.
|
@@ -749,7 +793,7 @@ module.exports = {
modules: {
appearance: ['responsive'],
backgroundAttachment: ['responsive'],
- backgroundColors: ['responsive', 'hover'],
+ backgroundColors: ['responsive', 'hover', 'group-hover'],
backgroundPosition: ['responsive'],
backgroundRepeat: ['responsive'],
backgroundSize: ['responsive'],
@@ -782,7 +826,7 @@ module.exports = {
svgFill: [],
svgStroke: [],
textAlign: ['responsive'],
- textColors: ['responsive', 'hover'],
+ textColors: ['responsive', 'hover', 'group-hover'],
textSizes: ['responsive'],
textStyle: ['responsive', 'hover'],
tracking: ['responsive'],
@@ -794,6 +838,26 @@ module.exports = {
zIndex: ['responsive'],
},
+ /*
+ |-----------------------------------------------------------------------------
+ | Plugins https://tailwindcss.com/docs/plugins
+ |-----------------------------------------------------------------------------
+ |
+ | Here is where you can register any plugins you'd like to use in your
+ | project. Tailwind's built-in `container` plugin is enabled by default to
+ | give you a Bootstrap-style responsive container component out of the box.
+ |
+ | Be sure to view the complete plugin documentation to learn more about how
+ | the plugin system works.
+ |
+ */
+
+ plugins: [
+ // require('tailwindcss/plugins/container')({
+ // center: true,
+ // padding: '1rem',
+ // }),
+ ],
/*
|-----------------------------------------------------------------------------
We’ve updated the configuration file and added a few new things. The new colors we added will be used by Tailwind to generate all the utility classes for each one. We also added some new maxWidth and margin properties.
An important change we did, was adding a new state variant for our backgroundColors and textColors utility classes. By default, Tailwind only generates classes for the responsive and hover state variants, but we added the group-hover
state.
In a previous commit we updated our npm packages, bumping up our Tailwind dependency to 0.5. This new release allowed us to customize our background size utility classes, use the new plugin system, added support to the active
state and many more interesting things. You can take a look at the release notes here.
We just created our new colors, let’s update our views to use them. We’ll start by updating our login.blade.php
file.
--- a/resources/views/auth/login.blade.php
+++ b/resources/views/auth/login.blade.php
@@ -5,14 +5,14 @@
@section('body')
<div class="h-2 bg-primary"></div>
- <div class="container mx-auto p-8">
+ <div class="container p-8">
<div class="mx-auto max-w-sm">
<div class="py-10 text-center">
@include('logo', ['style' => 'max-width: 12rem;'])
</div>
<div class="bg-white rounded shadow">
- <div class="border-b py-8 font-bold text-black text-center text-xl tracking-widest uppercase">
+ <div class="border-b border-grey-lighter py-8 font-bold text-black text-center text-xl tracking-widest uppercase">
Welcome back!
</div>
@@ -20,25 +20,25 @@
{{ csrf_field() }}
<div class="mb-3">
- <input class="border {{ $errors->first('email') ? 'border-red' : '' }} w-full p-3" name="email" type="text" placeholder="E-Mail">
+ <input class="rounded border border-grey-lighter {{ $errors->first('email') ? 'border-danger' : '' }} w-full p-3" name="email" type="text" placeholder="E-Mail">
@if ($errors->first('email'))
- <p class="text-red text-sm mt-1">{{ $errors->first('email') }}</p>
+ <p class="text-danger text-sm mt-1">{{ $errors->first('email') }}</p>
@endif
</div>
<div class="mb-6">
- <input class="border {{ $errors->first('password') ? 'border-red' : '' }} w-full p-3" name="password" type="password" placeholder="**************">
+ <input class="rounded border border-grey-lighter {{ $errors->first('password') ? 'border-danger' : '' }} w-full p-3" name="password" type="password" placeholder="**************">
@if ($errors->first('password'))
- <p class="text-red text-sm mt-1">{{ $errors->first('password') }}</p>
+ <p class="text-danger text-sm mt-1">{{ $errors->first('password') }}</p>
@endif
</div>
<div class="flex">
- <button type="submit" class="bg-primary hover:bg-primary-dark w-full p-4 text-sm text-white uppercase font-bold tracking-wider">
+ <button type="submit" class="rounded bg-primary hover:bg-primary-dark w-full p-4 text-sm text-white uppercase font-bold tracking-wider">
Login
</button>
</div>
</form>
- <div class="border-t px-10 py-6">
+ <div class="border-t border-grey-lighter px-10 py-6">
<div class="flex justify-between">
<a href="#" class="font-bold text-primary hover:text-primary-dark no-underline">Don't have an account?</a>
<a href="#" class="text-grey-darkest hover:text-black no-underline">Forgot Password?</a>
Here we simply changed our classes to use the new utilities we configured for Tailwind.
We did a lot on this episode and I’ve only highlighted the most important parts here. Feel free to take a look at the commit for this episode to see every change in more detail. You can find the commit here.