Laravel 5

Changer le framework CSS

Par défaut lorsqu’on installe Laravel et qu’on utilise la commande php artisan make:auth on obtient des vues donc le CSS est géré par Bootstrap 3. Même si c’est un excellent framework et sans doute le plus utilisé on peut avoir envie d’autre chose. Dans cet article on va voir comment on peut faire ce changement en conservant les possibilités de Laravel Mix.

Pour vous faciliter la vie le projet complet est téléchargeable ici.

Installation par défaut

Partez d’une nouvelle installation de Laravel :

composer create-project --prefer-dist laravel/laravel laravel5

Puis générez les vue pour l’authentification :

php artisan make:auth

Renseignez le fichier .env pour la connexion à la base puis générez les migrations :

php artisan migrate

Si tout se passe bien vous avez 3 tables :

Ensuite installez les modules avec npm :

npm install

Vous devez obtenir un dossier node_modules bien garni :

Jetez un coup d’œil au fichier package.json pour voir les scripts disponibles et les dépendances chargées :

{
    "private": true,
    "scripts": {
        "dev": "npm run development",
        "development": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
        "watch": "cross-env NODE_ENV=development node_modules/webpack/bin/webpack.js --watch --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js",
        "watch-poll": "npm run watch -- --watch-poll",
        "hot": "cross-env NODE_ENV=development node_modules/webpack-dev-server/bin/webpack-dev-server.js --inline --hot --config=node_modules/laravel-mix/setup/webpack.config.js",
        "prod": "npm run production",
        "production": "cross-env NODE_ENV=production node_modules/webpack/bin/webpack.js --progress --hide-modules --config=node_modules/laravel-mix/setup/webpack.config.js"
    },
    "devDependencies": {
        "axios": "^0.16.2",
        "bootstrap-sass": "^3.3.7",
        "cross-env": "^5.0.1",
        "jquery": "^3.1.1",
        "laravel-mix": "^1.0",
        "lodash": "^4.17.4",
        "vue": "^2.1.10"
    }
}

Lancez en mode développement pour voir si ça fonctionne :

npm run dev

Vous devez avoir la compilation des assets dans les fichiers public/css/app.css et public/js/app.js comme c’est prévu dans le fichier webpack.mix.js :

mix.js('resources/assets/js/app.js', 'public/js')
   .sass('resources/assets/sass/app.scss', 'public/css');

Pour mémoire les sources des assets sont ici :

Pour le CSS on voit que dans le fichier resources/assets/sass/app.scss on importe bootstrap :

@import "~bootstrap-sass/assets/stylesheets/bootstrap";

Et qu’on le modifie un peu en chargeant de nouvelles variables :

@import "variables";

Pour le Javascript on voit que dans le fichier resources/assets/js/bootstrap.js on charge bootstrap :

require('bootstrap-sass');

D’autre part on charge aussi lodash, jQuery et Vue.js avec un composant par défaut mais ce n’est pas l’objet du présent article.

Voilà donc un petit état des lieux à l’installation de Laravel et on se retrouve pour les vues de l’authentification avec un aspect typique de Bootstrap :

On va maintenant procéder aux modifications pour utiliser non plus Bootstrap mais Materialize que j’aime bien. Mais les explications restent valables pour n’importe quel framework CSS.

On passe à Materialize

L’intendance

Sur le site de npm on cherche le module :

Et comme on veut ce module uniquement pour le développement on va utiliser cette syntaxe :

npm install materialize-css --save-dev

Si tout va bien le fichier package.json est actualisé :

"devDependencies": {
    ...
    "materialize-css": "^0.100.2",
    ...
}

On va maintenant mettre à jour le fichier resources/assets/sass/app.scss pour importer Materialize plutôt que Bootstrap :

// Fonts
//@import url("https://fonts.googleapis.com/css?family=Raleway:300,400,600");
@import url("https://fonts.googleapis.com/icon?family=Material+Icons");

// Variables
//@import "variables";

// Bootstrap
//@import "~bootstrap-sass/assets/stylesheets/bootstrap";

//Materialize
@import "~materialize-css/sass/materialize.scss";

Et pour le Javascript ça se passe dans le fichier resources/assets/js/bootstrap.js :

//require('bootstrap-sass');
require('materialize-css');

On va lancer en mode développement avec actualisation automatique :

npm run watch

Si on regarde maintenant la page de login on va lui trouver un salle aspect :

C’est normal parce qu’on a les classes de Bootstrap déclarées et qu’on a plus ce framework disponible. Il nous reste maintenant à modifier les vues avec les classes de Materialize.

Loyout

Voici la nouvelle vue resources/views/layouts/app.blade.php :

<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}">
<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1">

    <!-- CSRF Token -->
    <meta name="csrf-token" content="{{ csrf_token() }}">

    <title>{{ config('app.name', 'Laravel') }}</title>

    <!-- Styles -->
    <link href="{{ asset('css/app.css') }}" rel="stylesheet">    
    @yield('css')
</head>
<body>
    <div id="app">

        @auth
            <ul id="dropdown1" class="dropdown-content">
                <li>
                    <a href="{{ route('logout') }}"
                        onclick="event.preventDefault();
                                 document.getElementById('logout-form').submit();">
                        Logout
                    </a>

                    <form id="logout-form" action="{{ route('logout') }}" method="POST" style="display: none;">
                        {{ csrf_field() }}
                    </form>
                </li>
            </ul>
        @endauth

        <nav>
            <div class="nav-wrapper">
                <a href="{{ url('/') }}" class="brand-logo">&nbsp{{ config('app.name', 'Laravel') }}</a>
                <a href="#" data-activates="mobile-demo" class="button-collapse"><i class="material-icons">menu</i></a>
                @guest
                    <ul class="right hide-on-med-and-down">
                        <li><a href="{{ route('login') }}">Login</a></li>
                        <li><a href="{{ route('register') }}">Register</a></li>
                    </ul>
                    <ul class="side-nav" id="mobile-demo">
                        <li><a href="{{ route('login') }}">Login</a></li>
                        <li><a href="{{ route('register') }}">Register</a></li>
                    </ul>
                @else
                    <ul class="right hide-on-med-and-down">
                        <li><a class="dropdown-button" href="#!" data-activates="dropdown1">{{ Auth::user()->name }}<i class="material-icons right">arrow_drop_down</i></a></li>
                    </ul>
                    <ul class="right hide-on-med-and-down">
                        <li><a class="dropdown-button" href="#!" data-activates="dropdown1">{{ Auth::user()->name }}<i class="material-icons right">arrow_drop_down</i></a></li>
                    </ul>
                @endguest
            </div>
        </nav>
        @yield('content')
    </div>

    <!-- Scripts -->
    <script src="{{ asset('js/app.js') }}"></script>
    <script>
        $(".button-collapse").sideNav()
        $(".dropdown-button").dropdown()
    </script>
</body>
</html>

J’ai changé essentiellement la barre de navigation. Ça commence à changer l’aspect :

Il faut maintenant modifier les autres vues…

Login

Voici la nouvelle vue de login (resources/views/auth/login.blade.php) :

@extends('layouts.app')

@section('css')
    <style>
        .card {
          margin-top: 40px;  
        }
    </style>
@endsection

@section('content')
<div class="container">
    <div class="row">
        <div class="card">
            <form  method="POST" action="{{ route('login') }}"> 
                <div class="card-content">   
                    {{ csrf_field() }}
                    <span class="card-title">Login</span>
                    <hr>
                    <div class="row">
                        <div class="input-field col s12">
                            <i class="material-icons prefix">mail</i>
                            <input id="email" type="email" name="email" value="{{ old('email') }}" class="{{ $errors->has('email') ? 'invalid' : '' }}" required autofocus>
                            <label for="email" data-error="{{ $errors->has('email') ? $errors->first('email'): '' }}">E-Mail Address</label>
                        </div>
                    </div>
                    <div class="row">
                        <div class="input-field col s12">
                            <i class="material-icons prefix">lock</i>
                            <input id="password" type="password" name="password" class="{{ $errors->has('password') ? 'invalid' : '' }}" required>
                            <label for="password" data-error="{{ $errors->has('password') ? $errors->first('password'): '' }}">Password</label>
                        </div>
                    </div>

                    <p>
                        <input type="checkbox" id="remember" {{ old('remember') ? 'checked' : '' }}>
                        <label for="remember">Remember Me</label>
                    </p>
                </div>
                <div class="card-action">
                    <button class="btn waves-effect waves-light" type="submit" name="action">Login
                        <i class="material-icons right">lock_open</i>
                    </button>
                    <a class="waves-effect waves-light btn" href="{{ route('password.request') }}">Forgot Your Password?<i class="material-icons right">message</i></a>
                </div>
            </form>    
        </div>
    </div>
</div>
@endsection

Avec cet aspect :

C’est quand même plus présentable !

Register

Voici la nouvelle vue d’enregistrement (resources/views/auth/register.blade.php) :

@extends('layouts.app')

@section('css')
    <style>
        .card {
          margin-top: 40px;  
        }
    </style>
@endsection

@section('content')
<div class="container">
    <div class="row">
        <div class="card">
            <form  method="POST" action="{{ route('register') }}"> 
                <div class="card-content">   
                    {{ csrf_field() }}
                    <span class="card-title">Register</span>
                    <hr>
                    <div class="row">
                        <div class="input-field col s12">
                            <i class="material-icons prefix">person</i>
                            <input id="name" type="text" name="name" value="{{ old('name') }}" class="{{ $errors->has('name') ? 'invalid' : '' }}" required autofocus>
                            <label for="name" data-error="{{ $errors->has('name') ? $errors->first('name'): '' }}">Name</label>
                        </div>
                    </div>
                    <div class="row">
                        <div class="input-field col s12">
                            <i class="material-icons prefix">mail</i>
                            <input id="email" type="email" name="email" value="{{ old('email') }}" class="{{ $errors->has('email') ? 'invalid' : '' }}" required autofocus>
                            <label for="email" data-error="{{ $errors->has('email') ? $errors->first('email'): '' }}">E-Mail Address</label>
                        </div>
                    </div>
                    <div class="row">
                        <div class="input-field col s12">
                            <i class="material-icons prefix">lock</i>
                            <input id="password" type="password" name="password" class="{{ $errors->has('password') ? 'invalid' : '' }}" required>
                            <label for="password" data-error="{{ $errors->has('password') ? $errors->first('password'): '' }}">Password</label>
                        </div>
                    </div>
                    <div class="row">
                        <div class="input-field col s12">
                            <i class="material-icons prefix">lock</i>
                            <input id="password-confirm" type="password" name="password_confirmation" required>
                            <label for="password-confirm">Confirm Password</label>
                        </div>
                    </div>
                </div>
                <div class="card-action">
                    <button class="btn waves-effect waves-light" type="submit" name="action">Register
                        <i class="material-icons right">create</i>
                    </button>
                </div>
            </form>    
        </div>
    </div>
</div>
@endsection

Avec cet aspect :

Email

Voici la nouvelle vue de demande de renouvellement du mot de passe (resources/views/auth/passwords/email.blade.php) :

@extends('layouts.app')

@section('css')
    <style>
        .row > .card {
          margin-top: 40px;  
        }
    </style>
@endsection

@section('content')
<div class="container">
    <div class="row">
        <div class="card">
            <form  method="POST" action="{{ route('password.email') }}"> 
                <div class="card-content">
                    {{ csrf_field() }}
                    @if (session('status'))
                        <div class="card green darken-1">
                            <div class="card-content white-text">
                                {{ session('status') }}
                            </div>
                        </div>
                    @endif
                    <span class="card-title">Reset Password</span>
                    <hr>
                    <div class="row">
                        <div class="input-field col s12">
                            <i class="material-icons prefix">mail</i>
                            <input id="email" type="email" name="email" value="{{ old('email') }}" class="{{ $errors->has('email') ? 'invalid' : '' }}" required autofocus>
                            <label for="email" data-error="{{ $errors->has('email') ? $errors->first('email'): '' }}">E-Mail Address</label>
                        </div>
                    </div>
                </div>
                <div class="card-action">
                    <button class="btn waves-effect waves-light" type="submit" name="action">Send Password Reset Link
                        <i class="material-icons right">lock_open</i>
                    </button>
                </div>
            </form>    
        </div>
    </div>
</div>
@endsection

Avec cet aspect :

Reset

Voici la nouvelle vue de de renouvellement du mot de passe (resources/views/auth/passwords/reset.blade.php) :

@extends('layouts.app')

@section('css')
    <style>
        .card {
          margin-top: 40px;  
        }
    </style>
@endsection

@section('content')
<div class="container">
    <div class="row">
        <div class="card">
            <form  method="POST" action="{{ route('password.request') }}"> 
                <div class="card-content">   
                    {{ csrf_field() }}

                    <input type="hidden" name="token" value="{{ $token }}">

                    <span class="card-title">Reset Password</span>
                    <hr>
                    <div class="row">
                        <div class="input-field col s12">
                            <i class="material-icons prefix">mail</i>
                            <input id="email" type="email" name="email" value="{{ old('email') }}" class="{{ $errors->has('email') ? 'invalid' : '' }}" required autofocus>
                            <label for="email" data-error="{{ $errors->has('email') ? $errors->first('email'): '' }}">E-Mail Address</label>
                        </div>
                    </div>
                    <div class="row">
                        <div class="input-field col s12">
                            <i class="material-icons prefix">lock</i>
                            <input id="password" type="password" name="password" class="{{ $errors->has('password') ? 'invalid' : '' }}" required>
                            <label for="password" data-error="{{ $errors->has('password') ? $errors->first('password'): '' }}">Password</label>
                        </div>
                    </div>
                    <div class="row">
                        <div class="input-field col s12">
                            <i class="material-icons prefix">lock</i>
                            <input id="password-confirm" type="password" name="password_confirmation" required>
                            <label for="password-confirm">Confirm Password</label>
                        </div>
                    </div>
                </div>
                <div class="card-action">
                    <button class="btn waves-effect waves-light" type="submit" name="action">Reset Password
                        <i class="material-icons right">lock_open</i>
                    </button>
                </div>
            </form>    
        </div>
    </div>
</div>
@endsection

Avec cet aspect :

Home

Pour finir voilà le code pour la vue resources/views/home.blade.php :

@extends('layouts.app')

@section('content')
<div class="container">
  <div class="row">
    <div class="col s12 m6"> 
    @if (session('status'))
        <div class="card green darken-1">
            <div class="card-content white-text">
                {{ session('status') }}
            </div>
        </div>
    @endif
      <div class="card red lighten-2">
        <div class="card-content white-text">

            <span class="card-title">Dashboard</span>
            You are logged in!
        </div>
      </div>
    </div>
  </div>
</div>
@endsection

Avec cet aspect :

Vous voyez que le changement de framework CSS n’est pas bien difficile mais il demande un peu d’attention au niveau de Laravel Mix si on veut profiter de cet outil…

Print Friendly, PDF & Email

2 commentaires

  • CGO

    Bonjour j’ai suivi votre tuto, est impossible d’afficher le menu en version mobile, de plus les onglets « connexion » et « s’inscrire » ne s’affiche pas dans la nav. Une fois connecté le dropdown ne s’affiche pas.

    J’ai une erreur sur la fonction sideNav. je ne sais pas comment faire pour corriger ce problème

Laisser un commentaire