Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Docs] Steps on how to create a new Backpack v6 theme #417

Closed
tabacitu opened this issue Mar 31, 2023 · 3 comments
Closed

[Docs] Steps on how to create a new Backpack v6 theme #417

tabacitu opened this issue Mar 31, 2023 · 3 comments
Assignees

Comments

@tabacitu
Copy link
Member

For the first time ever... we'll have an official and supported way to turn an HTML template you buy from GetBootstrap, WrapBootstrap or ThemeForest to a Backpack theme ❤️

Our challenge here... is to make it FAST. Let's aim for 5 hours or less 🤞

@maurohmartinez
Copy link
Member

maurohmartinez commented Apr 3, 2023

Challenges/discoveries noted so far

  1. Certain packages need to be supported cross-theme. E.g.: Backpack uses Noty, SweetAlert, which initially were not included on my theme... and yes, it broke it! 💣
  2. List operation (tables) do not look good. That's because tables use different classes, so apparently the List Operation is the least theme-friendly so far 😞
  3. The used of fonts is a bit tricky. I had to manually edit the css files provided by the theme to update the right path to the fonts, and manually copy the fonts into public/assets/fonts directory.
  4. Re-creating the sidebar was the most time-consuming task... although it wasn't that bad!

[WIP]

Here are the steps to easily build a Backpack theme using a template you got from GetBootstrap, WrapBootstrap or ThemeForest.

1 - Create a view folder anywhere in your resources/views.

This is the directory where you will place all the files for your theme.

mkdir resources/views/my-cool-theme

2 - In your config/backpack/ui.php, add that as the primary view namespace:

    'view_namespace' => 'my-cool-theme.',

(!) Important
1 - Notice the . at the end of the namespace, that's important.
2 - The namespace must match the name of the folder created from the previous step.

3 - Choose and use a fallback theme

A fallback theme is needed in cases when Backpack attempts to load a view that doesn't exist in your theme. It means you don't need to create all the views in order to create a theme... Phew! You can easily rely on your fallback theme and only create the views you need to customize. In order to do so, edit your config file config/backpack/ui.php as it follows:

    'view_namespace' => 'my-cool-theme.',
    'view_namespace_fallback' => 'backpack.theme-coreuiv4::', // <--- this line

If it's your first time creating a Backpack theme, we recommend you start from our CoreUIv4 theme, and use that as your fallback. It's the simplest modern theme we have. It uses Bootstrap v5, but doesn't have any extra features you'd need to also support (like Tabler does). If you don't already have it installed, you will need to do composer require backpack/theme-coreuiv4

4 - Create main blade files

If you refresh the page right now, it will show up IDENTICAL to CoreUIv4. Why? Because you're using all the blade files from that theme (through the fallback system). How can you make your theme look like your theme? By overriding some of blade files the fallback theme provides. Similar to how child themes work in Wordpress.

Feel free to look at your fallback theme's views (eg. vendor/backpack/theme-coreuiv4/resources/views). If you create a file with the same file in your theme directory (eg. resources/views/my-cool-theme), your view will be picked up.

So let's do that. Let's create most of the files you'll need to customize, to provide your theme with your style:

- your-cool-theme/
  blank.blade.php
  - layouts/
      app.blade.php
  - widgets/
      ...here you can override widgets with your own!
  - assets/
      - css/
         ...here place all css files provided by your original purchased theme
      - js/
         ...here place all js files provided by your original purchased theme
  - inc/
      styles.blade.php
      scripts.blade.php
      header.blade.php
      menu.blade.php
      footer.blade.php

Now let's build those files...

your-cool-theme/blank.blade.php

From now on, Backpack will try to load this file. The purpose of this file is to load all Backpack widgets and to extend the layout.app of your theme. Without this file widgets won't be loaded across your project.

(!) Remember Backpack will fallback to your fallback theme set in your config file.

Copy this file and adjust it if you need it.

@extends(backpack_view('layouts.app'))

@php
    if (isset($widgets)) {
        foreach ($widgets as $section => $widgetSection) {
            foreach ($widgetSection as $key => $widget) {
                \Backpack\CRUD\app\Library\Widget::add($widget)->section($section);
            }
        }
    }
@endphp

@section('before_breadcrumbs_widgets')
    @include(backpack_view('inc.widgets'), [ 'widgets' => app('widgets')->where('section', 'before_breadcrumbs')->toArray() ])
@endsection

@section('after_breadcrumbs_widgets')
    @include(backpack_view('inc.widgets'), [ 'widgets' => app('widgets')->where('section', 'after_breadcrumbs')->toArray() ])
@endsection

@section('before_content_widgets')
    @include(backpack_view('inc.widgets'), [ 'widgets' => app('widgets')->where('section', 'before_content')->toArray() ])
@endsection

@section('content')
@endsection

@section('after_content_widgets')
    @include(backpack_view('inc.widgets'), [ 'widgets' => app('widgets')->where('section', 'after_content')->toArray() ])
@endsection

your-cool-theme/layouts/app.blade.php

This file should contain the layout for your theme, starting with the HTML doctype declaration, head, and body components. The following example will help you get started. Feel free to copy it!

<!DOCTYPE html>

<html lang="{{ app()->getLocale() }}" dir="{{ backpack_theme_config('html_direction') }}">

<head>
    <meta charset="utf-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0, shrink-to-fit=no">
    <meta name="csrf-token" content="{{ csrf_token() }}"/>
    <title>{{ isset($title) ? $title.' :: '.backpack_theme_config('project_name') : backpack_theme_config('project_name') }}</title>

    @yield('before_styles')
    @stack('before_styles')

    @include(backpack_view('inc.styles'))

    @yield('after_styles')
    @stack('after_styles')
</head>

<body>

<div>
    @yield('before_breadcrumbs_widgets')
    @includeWhen(isset($breadcrumbs), backpack_view('inc.breadcrumbs'))
    @yield('after_breadcrumbs_widgets')

    <div>
        @yield('before_content_widgets')
        @yield('content')
        @yield('after_content_widgets')
    </div>
</div>

@yield('before_scripts')
@stack('before_scripts')

@include(backpack_view('inc.scripts'))

@yield('after_scripts')
@stack('after_scripts')
</body>
</html>

your-cool-theme/inc/styles.blade.php

(!) Make sure you have run php artisan storage:link, otherwise Backpack won't be able to load the generated css files.

Bassets allows you to load assets hosted on your project or remotely.

In your project:
@basset(base_path('resources/your-cool-theme/assets/css/name-of-the-file-1.css'))
@basset(base_path('resources/your-cool-theme/assets/css/name-of-the-file-2.css'))
... and so on.

Remotely:
@basset('https://unpkg.com/[email protected]/lib/noty.css')

(!) When creating this file, make sure you include the following lines since they are required by Backpack!

{{-- Required by Backpack --}}
@basset('https://unpkg.com/[email protected]/lib/noty.css')
@basset('https://unpkg.com/[email protected]/animate.compat.css')
@basset('https://cdn.jsdelivr.net/npm/[email protected]/themes/prism.css')

your-cool-theme/inc/scripts.blade.php

Following the same principle as with styles, include your js files using bassets.

(!) When creating this file, make sure you include the following lines since they are required by Backpack! These will become a different file to load only once, but for now, include all manually.

{{-- Required by Backpack --}}
@basset('https://unpkg.com/[email protected]/lib/noty.min.js')
@basset('https://unpkg.com/[email protected]/dist/sweetalert.min.js')

@if (backpack_theme_config('scripts') && count(backpack_theme_config('scripts')))
    @foreach (backpack_theme_config('scripts') as $path)
        @if(is_array($path))
            @basset(...$path)
        @else
            @basset($path)
        @endif
    @endforeach
@endif

@if (backpack_theme_config('mix_scripts') && count(backpack_theme_config('mix_scripts')))
    @foreach (backpack_theme_config('mix_scripts') as $path => $manifest)
        <script type="text/javascript" src="{{ mix($path, $manifest) }}"></script>
    @endforeach
@endif

@if (backpack_theme_config('vite_scripts') && count(backpack_theme_config('vite_scripts')))
    @vite(backpack_theme_config('vite_scripts'))
@endif

@include(backpack_view('inc.alerts'))

{{-- page script --}}
<script type="text/javascript">
    // To make Pace works on Ajax calls
    $(document).ajaxStart(function() { Pace.restart(); });

    // polyfill for `startsWith` from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/String/startsWith
    if (!String.prototype.startsWith) {
        Object.defineProperty(String.prototype, 'startsWith', {
            value: function(search, rawPos) {
                var pos = rawPos > 0 ? rawPos|0 : 0;
                return this.substring(pos, pos + search.length) === search;
            }
        });
    }



    // polyfill for entries and keys from https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/entries#polyfill
    if (!Object.keys) Object.keys = function(o) {
        if (o !== Object(o))
            throw new TypeError('Object.keys called on a non-object');
        var k=[],p;
        for (p in o) if (Object.prototype.hasOwnProperty.call(o,p)) k.push(p);
        return k;
    }

    if (!Object.entries) {
        Object.entries = function( obj ){
            var ownProps = Object.keys( obj ),
                i = ownProps.length,
                resArray = new Array(i); // preallocate the Array
            while (i--)
                resArray[i] = [ownProps[i], obj[ownProps[i]]];
            return resArray;
        };
    }

    // Ajax calls should always have the CSRF token attached to them, otherwise they won't work
    $.ajaxSetup({
        headers: {
            'X-CSRF-TOKEN': $('meta[name="csrf-token"]').attr('content')
        }
    });

    // Enable deep link to tab
    document.querySelectorAll('.nav-tabs a').forEach(function (elem) {
        if(elem.dataset.name === location.hash.substr(1)) (new bootstrap.Tab(elem)).show();
        elem.addEventListener('click', () => location.hash = elem.dataset.name);
    });
</script>

@if(config('app.debug'))
    @include('crud::inc.ajax_error_frame')
@endif

your-cool-theme/inc/header.blade.php

Build your html for the header of your theme.

your-cool-theme/inc/menu.blade.php

Build your html for the menu of your theme.

your-cool-theme/inc/footer.blade.php

Build your html for the footer of your theme.

@maurohmartinez maurohmartinez changed the title Steps on how to create a new Backpack v6 theme [Feature Request][tabler] Steps on how to create a new Backpack v6 theme Apr 11, 2023
@maurohmartinez maurohmartinez changed the title [Feature Request][tabler] Steps on how to create a new Backpack v6 theme [v6][Feature Request][Tabler] Steps on how to create a new Backpack v6 theme Apr 11, 2023
@maurohmartinez maurohmartinez changed the title [v6][Feature Request][Tabler] Steps on how to create a new Backpack v6 theme [Feature Request][Tabler] Steps on how to create a new Backpack v6 theme Apr 11, 2023
@tabacitu tabacitu assigned tabacitu and unassigned maurohmartinez May 30, 2023
@tabacitu tabacitu added the Size: M 1 week label Jun 20, 2023
@tabacitu
Copy link
Member Author

Update tutorial:


Here are the steps to easily build a Backpack theme using a template you got from GetBootstrap, WrapBootstrap or ThemeForest.

1 - Create a view folder anywhere in your resources/views.

This is the directory where you will place all the files for your theme.

mkdir resources/views/my-cool-theme

2 - In your config/backpack/ui.php, add that as the primary view namespace:

    'view_namespace' => 'my-cool-theme.',

(!) Important
1 - Notice the . at the end of the namespace, that's important.
2 - The namespace must match the name of the folder created from the previous step.

3 - Choose and use a fallback theme

A fallback theme is needed in cases when Backpack attempts to load a view that doesn't exist in your theme. It means you don't need to create all the views in order to create a theme... Phew! You can easily rely on your fallback theme and only create the views you need to customize. In order to do so, edit your config file config/backpack/ui.php as it follows:

    'view_namespace' => 'my-cool-theme.',
    'view_namespace_fallback' => 'backpack.theme-coreuiv4::', // <--- this line

If it's your first time creating a Backpack theme, we recommend you start from our CoreUIv4 theme, and use that as your fallback. It's the simplest modern theme we have. It uses Bootstrap v5, but doesn't have any extra features you'd need to also support (like Tabler does). If you don't already have it installed, you will need to do composer require backpack/theme-coreuiv4

4 - Create main blade files

If you refresh the page right now, it will show up IDENTICAL to CoreUIv4. Why? Because you're using all the blade files from that theme (through the fallback system). How can you make your theme look like your theme? By overriding some of blade files the fallback theme provides. Similar to how child themes work in Wordpress.

Feel free to look at your fallback theme's views (eg. vendor/backpack/theme-coreuiv4/resources/views). If you create a file with the same file in your theme directory (eg. resources/views/my-cool-theme), your view will be picked up.

So let's do that. Let's create most of the files you'll need to customize, to provide your theme with your style:

- my-cool-theme/
  - layouts/
      app.blade.php
  - widgets/
      ...here you can override widgets with your own!
  - components/
      ...here you can override widgets with your own!
  - assets/
      - css/
         ...here place all css files provided by your original purchased theme
      - js/
         ...here place all js files provided by your original purchased theme
  - inc/
      styles.blade.php
      scripts.blade.php
      header.blade.php
      menu.blade.php
      footer.blade.php

Now let's build those files... Let's start with what makes a theme different.

my-cool-theme/inc/theme_styles.blade.php

This file should hold all custom CSS that your theme needs. For example:

{{-- You can load files directly from CDNs, they will get cached and loaded from local --}}
@basset('https://cdn.jsdelivr.net/npm/[email protected]/dist/css/bootstrap.min.css')

{{-- You can also load files from any place in your application directories --}}
@basset(views_path('my-cool-theme/assets/css/extra.css'))

{{-- You can also write inline CSS blocks --}}
@bassetBlock('my-cool-theme/custom-styling')
<style>
.something {
    border: 1px solid red;
}
</style>
@endBassetBlock

Note: Don't forget to load the Bootstrap CSS. Backpack does NOT load it by default.

my-cool-theme/inc/theme_scripts.blade.php

This file should hold all custom JS that your theme needs:

{{-- You can load files directly from CDNs, they will get cached and loaded from local --}}
@basset('https://cdn.jsdelivr.net/npm/[email protected]/dist/js/bootstrap.bundle.min.js')

{{-- You can also load files from any place in your application directories --}}
@basset(views_path('my-cool-theme/assets/css/extra.js'))

{{-- You can also write inline JS blocks --}}
@bassetBlock('my-cool-theme/custom-scripting')
<script>
 alert('got here');
</script>
@endBassetBlock

Note: Don't forget to load the Bootstrap JS. Backpack does NOT load it by default.

my-cool-theme/layouts/default.blade.php

This will be the primary layout for your theme. So it has to contain a full HTML page: the HTML doctype declaration, head, body components, everything. The following example will help you get started.

<!DOCTYPE html>
<html lang="{{ app()->getLocale() }}" dir="{{ backpack_theme_config('html_direction') }}">
<head>
    @include(backpack_view('inc.head'))
</head>
<body class="{{ backpack_theme_config('classes.body') }}">

    @include(backpack_view('inc.sidebar'))

    <div class="wrapper d-flex flex-column min-vh-100 bg-light">

        @include(backpack_view('inc.main_header'))

        <div class="app-body flex-grow-1 px-2">

            <main class="main">

                @yield('before_breadcrumbs_widgets')
                @includeWhen(isset($breadcrumbs), backpack_view('inc.breadcrumbs'))
                @yield('after_breadcrumbs_widgets')

                @yield('header')

                <div class="container-fluid animated fadeIn">

                    @yield('before_content_widgets')
                    @yield('content')
                    @yield('after_content_widgets')

                </div>

            </main>

        </div>{{-- ./app-body --}}

        <footer class="{{ backpack_theme_config('classes.footer') }}">
            @include(backpack_view('inc.footer'))
        </footer>
    </div>

    @include(backpack_view('inc.bottom'))
</body>
</html>

We recommend you copy-paste your own HTML above it, then include the @directives where they make sense in your layout. Their names should explain pretty well what they do.

Next up, we'll have to drill down. And move any custom content that's needed for the layout... for example for the sidebar, the header, the topbar... into their own respective views.

my-cool-theme/inc/head.blade.php

There should be no reason for you to customize this file.

my-cool-theme/inc/sidebar.blade.php

We recommend you:

  • create the file;
  • paste the main_header.blade.php from your fallback theme;
  • paste the HTML content you want;
  • copy the useful things from the fallback theme to your own HTML, then delete whatever you don't want;

Do not drill down to customize sidebar_content.blade.php too. Menu items work a little different than other views - they are view components. So instead of customizing sidebar_content.blade.php take a few minutes and customize the menu items HTML, by copy-pasting the components directory from your fallback theme, then customizing the files inside it (menu-item, menu-dropdown, menu-dropdown-item, menu-separator etc).

my-cool-theme/inc/main_header.blade.php

We recommend you:

  • create the file;
  • paste the main_header.blade.php from your fallback theme;
  • paste the HTML content you want;
  • copy the useful things from the fallback theme to your own HTML, then delete whatever you don't want;

my-cool-theme/inc/breadcrumbs.blade.php

Most often this will look ok out-of-box, no need to customize. But if you do need to customize the breadcrumbs, follow the same process above to re-use everything you need from the fallback theme file.

my-cool-theme/inc/footer.blade.php

Most often this will look ok out-of-box, no need to customize. But if you do need to customize the breadcrumbs, follow the same process above to re-use everything you need from the fallback theme file.

my-cool-theme/inc/bottom.blade.php

There should be no reason for you to customize this file.

@tabacitu
Copy link
Member Author

tabacitu commented Jun 30, 2023

Update:

This will provide 80% of the info needed to create a new theme.

HOWEVER. We can still do A LOT more to improve how fast or easy it is to create a theme. We need to create a few themes, going through the tutorial, and improve the tutorial, improve the skeleton, improve the process. The faster we make this, the faster it will be for us to create new themes and for others to do the same.

To be continued... in #448

@tabacitu tabacitu changed the title [Feature Request][Tabler] Steps on how to create a new Backpack v6 theme [Docs] Steps on how to create a new Backpack v6 theme Jun 30, 2023
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
Status: Done
Development

No branches or pull requests

2 participants