Laravel E-Shop with Bagisto: Custom Design Theme

Laravel E-Shop with Bagisto: Custom Design Theme
Admin
Tuesday, July 25, 2023 9 mins to read
Share
Laravel E-Shop with Bagisto: Custom Design Theme

Bagisto is one of the most popular Laravel e-commerce systems. In this tutorial, we will try to build a small demo e-shop with it, step-by-step, creating a custom design theme.

The example shop idea is taken from a "former official" store of Laravel products. At the time of writing this article, the Laravel team is rebuilding a new store, so not sure if this link is still working in the future, but: laravel.bigcartel.com

The end goal will look something like this:

This Laravel merchandise store has this functionality:

  • Custom design
  • Product lists
  • Product preview pages
  • Product variation
  • Basket
  • Checkout

And to build it, we'll have to:

  1. Install Bagisto
  2. Add a custom theme
  3. Add products

Notice: in this tutorial, we will not touch the checkout and payment processes, only the visual front look of the store with products. Maybe a topic for a future follow-up.


Installation

To install Bagisto, we've followed the official installation guide:

composer create-project bagisto/bagisto

After that, we modified our .env file filling database credentials. Then we've run migrations:

php artisan migrate --seed

This already set quite a lot of things in place, but we've also had to run:

php artisan storage:link

To make sure that our images are accessible.

And lastly, we've just published all the assets:

php artisan vendor:publish --all

This step might have been overkill, but it's on the guide, so we've done it. This resulted in a store that looks like this:


Creating a Custom Theme

Now that we have a store, we must make it look like the one we've shown in the beginning. To do that, we have to create a theme. This can be done like this:

Copy the folder resources/themes/default to resources/themes/laravelshop (or any other name you need)

Copy the folder public/themes/default to public/themes/laravelshop (or any other name you need)

Then open up config/themes.php and add your theme to the list:

<?php
 
return [
'default' => 'default',
 
'themes' => [
'default' => [
'views_path' => 'resources/themes/default/views',
'assets_path' => 'public/themes/default/assets',
'name' => 'Default'
],
 
'velocity' => [
'views_path' => 'resources/themes/velocity/views',
'assets_path' => 'public/themes/velocity/assets',
'name' => 'Velocity',
'parent' => 'default'
],
 
'laravelshop' => [
'views_path' => 'resources/themes/laravelshop/views',
'assets_path' => 'public/themes/laravelshop/assets',
'name' => 'LaravelShop',
'parent' => 'default'
],
],
 
'admin-default' => 'default',
 
'admin-themes' => [
'default' => [
'views_path' => 'resources/admin-themes/default/views',
'assets_path' => 'public/admin-themes/default/assets',
'name' => 'Default'
]
]
];

The last step is to log in to the admin panel and set the theme to the one we've just created:

You need to navigate to Settings -> Channels:

Then edit the default channel by scrolling until you find the Theme field:

In this dropdown, you should select a theme named LaravelShop (or whatever you've named your theme in config/themes.php).

That's it! Now we have our custom theme running and can modify the shop!


Customizing the Theme

Now that our theme is live, we must make it unique and specifically created for us. We'll have to modify the files in the resources/themes/laravelshop/views folder to do that. This is where all the views are stored, and we can modify them to our liking.

Warning: looking from a pure Laravel MVC perspective, Bagisto Blade code may look like anti-pattern, because A LOT of logic is in Blade files. You will see many @php ... @endphp blocks and <?php calls in the Views, which is generally not recommended. But in this case, it makes perfect sense, as different blocks now are totally isolated and independent from other files.

Homepage

Let's start by modifying the homepage. The initial step is to change what is displayed on the homepage. To do that, we need to go into channel settings (we've changed the theme there) and change the Home Page Content value:

New value:

@include('shop::home.featured-products')

Then we need to clear the Footer Content input and save. This will make our homepage update look like this:

Which already made our job easier by removing a lot of unnecessary elements. Next is the actual customization of the template:

Fixing the Header

The first thing we need to fix is a cart link. By default, it is not linking to the view cart page, so we will change this:

resources/themes/laravelshop/views/checkout/cart/mini-cart.blade.php

@php
$cart = cart()->getCart();
@endphp
 
@if ($cart)
@php
$items = $cart->items;
@endphp
 
<a class="name" href="{{ route('shop.checkout.cart.index') }}">
{{ __('shop::app.header.cart') }}
<span class="count"> ({{ $cart->items->count() }})</span>
</a>
@else
<a class="name" href="{{ route('shop.checkout.cart.index') }}">{{ __('shop::app.minicart.cart') }}<span
class="count"> ({{ __('shop::app.minicart.zero') }}) </span></a>
@endif

This will give us a simple link to open the cart page. Next, we need to fix the whole navigation bar:

resources/themes/laravelshop/views/layouts/header/index.blade.php

<div class="header" id="header">
<div class="header-top">
<div class="right-content" style="width: 100%;">
<ul class="right-content-menu" style="width: 100%; text-align: center;">
 
<li>
<a href="{{ route('shop.home.index') }}">Products</a>
</li>
 
<li class="">
<a href="{{ route('shop.cms.page', ['contact-us']) }}">Contact</a>
</li>
 
{!! view_render_event('bagisto.shop.layout.header.cart-item.before') !!}
 
<li>
@include('shop::checkout.cart.mini-cart')
</li>
 
{!! view_render_event('bagisto.shop.layout.header.cart-item.after') !!}
 
</ul>
 
<span class="menu-box"><span class="icon icon-menu" id="hammenu"></span></span>
</div>
</div>
<div class="branding">
<a href="/" title="Home">
<img class="store-logo"
srcset="https://assets.bigcartel.com/theme_images/54651347/laravel-logo.png?auto=format&amp;fit=max&amp;h=400&amp;w=1068, https://assets.bigcartel.com/theme_images/54651347/laravel-logo.png?auto=format&amp;fit=max&amp;h=800&amp;w=2136 2x"
src="https://assets.bigcartel.com/theme_images/54651347/laravel-logo.png?auto=format&amp;fit=max&amp;h=400&amp;w=1068"
alt="laravel Home" style="max-height: 400px">
</a>
</div>
</div>

Along with some CSS changes (Yes, we are modifying CSS in a public folder as that's recommended in their video tutorial):

public/themes/laravelshop/assets/css/shop.css

/* ... */
 
.header .header-top div.right-content .right-content-menu {
-webkit-text-size-adjust: 100%;
color: #111111;
font-size: 16px;
line-height: 1;
text-align: center;
font-family: "Varela Round", sans-serif;
text-transform: uppercase;
letter-spacing: 0.5px;
box-sizing: border-box;
display: flex;
justify-content: center;
list-style: none;
margin: 0;
padding: 0;
}
 
.header .header-top div.right-content .right-content-menu > li {
-webkit-text-size-adjust: 100%;
color: #111111;
font-size: 16px;
line-height: 1;
font-family: "Varela Round", sans-serif;
text-transform: uppercase;
letter-spacing: 0.5px;
list-style: none;
box-sizing: border-box;
border-bottom: 1px solid #b1b1b1;
padding: 8px;
}
 
.header .header-top div.right-content .right-content-menu > li > a {
-webkit-text-size-adjust: 100%;
line-height: 1;
font-family: "Varela Round", sans-serif;
text-transform: uppercase;
letter-spacing: 0.5px;
list-style: none;
box-sizing: border-box;
background-color: transparent;
transition: color 0.1s linear;
cursor: pointer;
color: #555555;
display: block;
font-size: 15px;
padding: 8px;
text-decoration: none;
}
 
.branding {
-webkit-text-size-adjust: 100%;
color: #111111;
font-family: "Varela Round", sans-serif;
line-height: 1;
text-align: center;
box-sizing: border-box;
font-size: 20px;
margin: 76px auto;
text-transform: uppercase;
}
/* ... */

This will give us a header that looks like this:

Fixing the Footer

To fix the footer, we need to modify a few files:

resources/themes/laravelshop/views/layouts/footer/footer.blade.php

<footer>
<div class="wrapper">
<nav class="footer-nav" role="navigation" aria-label="Footer">
<ul class="footer-links">
<li><a href="{{ route('shop.home.index') }}">Home</a></li>
<li><a href="{{ route('shop.home.index') }}">Products</a></li>
 
<li><a href="{{ route('shop.cms.page', ['contact-us']) }}">Contact</a></li>
<li><a href="{{ route('shop.checkout.cart.index') }}">Cart</a></li>
 
</ul>
</nav>
</div>
</footer>

And add the following CSS:

public/themes/laravelshop/assets/css/shop.css

/* ... */
 
footer {
-webkit-text-size-adjust: 100%;
color: #111111;
font-family: "Varela Round", sans-serif;
font-size: 16px;
line-height: 1;
box-sizing: border-box;
display: block;
}
 
footer > .wrapper {
-webkit-text-size-adjust: 100%;
color: #111111;
font-family: "Varela Round", sans-serif;
font-size: 16px;
line-height: 1;
box-sizing: border-box;
padding: 0 24px;
margin: 0 auto;
max-width: 1100px;
width: 100%;
}
 
footer > .wrapper > .footer-nav {
-webkit-text-size-adjust: 100%;
color: #111111;
font-size: 16px;
line-height: 1;
box-sizing: border-box;
border-top: 1px solid #b1b1b1;
display: flex;
flex-direction: column;
align-items: center;
font-family: "Varela Round", sans-serif;
list-style: none;
gap: 32px;
margin: 0;
padding: 40px 0;
}
 
.footer-nav ul.footer-links {
-webkit-text-size-adjust: 100%;
color: #111111;
line-height: 1;
font-family: "Varela Round", sans-serif;
box-sizing: border-box;
display: flex;
justify-content: center;
flex-wrap: wrap;
list-style: none;
margin: 0 0 24px 0;
padding: 0;
font-size: 16px;
}
 
.footer-nav ul.footer-links > li {
-webkit-text-size-adjust: 100%;
color: #111111;
line-height: 1;
font-family: "Varela Round", sans-serif;
list-style: none;
font-size: 16px;
box-sizing: border-box;
}
 
.footer-nav ul.footer-links > li > a {
-webkit-text-size-adjust: 100%;
line-height: 1;
font-family: "Varela Round", sans-serif;
list-style: none;
font-size: 16px;
box-sizing: border-box;
background-color: transparent;
transition: color 0.1s linear;
color: #111111;
cursor: pointer;
display: block;
padding: 8px 16px;
text-decoration: none;
}

By doing this, we will have the following footer:

Working on the Products List

The last step to make our homepage look like the one in the demo is to update the product list. To do this, we need to modify the following files:

resources/themes/laravelshop/views/home/featured-products.blade.php

@php
request()->query->remove('new');
 
request()->query->add([
// 'featured' => 1,
'order' => 'rand',
'limit' => request()->get('count')
?? core()->getConfigData('catalog.products.homepage.no_of_featured_product_homepage'),
]);
 
$products = app(\Webkul\Product\Repositories\ProductRepository::class)->getAll();
@endphp
 
@if ($products->count())
<section class="featured-products">
 
<div class="featured-grid product-grid-4">
 
@foreach ($products as $productFlat)
 
@include ('shop::products.list.card', ['product' => $productFlat])
 
@endforeach
 
</div>
 
</section>
@endif

With CSS, we need to set the grid to three columns:

public/themes/laravelshop/assets/css/shop.css

/* ... */
 
 
.main-container-wrapper .product-grid-3, .main-container-wrapper .product-grid-4 {
display: grid;
grid-template-columns:repeat(auto-fill, minmax(30%, 1fr)); /* 30% of the container width */
justify-items: center
}

This brought us closer to what we need:

But we still need to style each product box:

resources/themes/laravelshop/views/products/list/card.blade.php

{!! view_render_event('bagisto.shop.products.list.card.before', ['product' => $product]) !!}
 
<div class="product-card">
 
<?php $productBaseImage = product_image()->getProductBaseImage($product); ?>
 
@if (
! $product->getTypeInstance()->haveDiscount()
&& $product->new
)
<div class="sticker new">
{{ __('shop::app.products.new') }}
</div>
@endif
 
<div class="product-image" style="background: none;">
<a href="{{ route('shop.productOrCategory.index', $product->url_key) }}" title="{{ $product->name }}">
<img src="{{ $productBaseImage['medium_image_url'] }}"
onerror="this.src='{{ asset('vendor/webkul/ui/assets/images/product/medium-product-placeholder.png') }}'"
alt="" height="500"/>
</a>
</div>
 
<div class="product-information">
 
<div class="product-list-thumb-info">
<div class="product-list-item-background"></div>
<div class="product-list-thumb-info-headers">
<div class="product-list-thumb-name">
<a
href="{{ route('shop.productOrCategory.index', $product->url_key) }}"
title="{{ $product->name }}">
<span>
{{ $product->name }}
</span>
</a>
</div>
<div class="product-list-thumb-price">
 
@include ('shop::products.price', ['product' => $product])
 
</div>
 
</div>
</div>
 
</div>
 
</div>
 
{!! view_render_event('bagisto.shop.products.list.card.after', ['product' => $product]) !!}

And, of course, add CSS to style it:

public/themes/laravelshop/assets/css/shop.css

/* ... */
 
 
.product-list-thumb-info {
-webkit-text-size-adjust: 100%;
font-family: "Varela Round", sans-serif;
word-break: break-word;
color: #111111;
cursor: pointer;
box-sizing: border-box;
font-size: 18px;
line-height: 1.25em;
padding: 16px 8px;
position: relative;
text-align: center;
}
 
.product-list-thumb-info-headers {
-webkit-text-size-adjust: 100%;
font-family: "Varela Round", sans-serif;
word-break: break-word;
color: #111111;
cursor: pointer;
font-size: 18px;
line-height: 1.25em;
text-align: center;
box-sizing: border-box;
}
 
.product-list-thumb-name {
-webkit-text-size-adjust: 100%;
font-family: "Varela Round", sans-serif;
word-break: break-word;
color: #111111;
cursor: pointer;
font-size: 18px;
line-height: 1.25em;
text-align: center;
box-sizing: border-box;
overflow-wrap: break-word;
position: relative;
text-transform: uppercase;
letter-spacing: 1px;
}
 
.product-list-thumb-name:after {
bottom: -8px;
content: "";
background-color: #111111;
height: 1px;
left: 50%;
opacity: 0.5;
position: absolute;
transform: translateX(-50%);
width: 16px;
}
 
.product-list-thumb-name a {
color: #111111;
}
 
.product-list-thumb-price {
-webkit-text-size-adjust: 100%;
word-break: break-word;
color: #111111;
cursor: pointer;
line-height: 1.25em;
text-align: center;
box-sizing: border-box;
font-family: "Montserrat", sans-serif;
font-size: 16px;
margin-top: 16px;
}

Then as a last step, we need to update how we display prices:

public/themes/laravelshop/assets/css/shop.css

/* ... */
 
.price-label {
display: none;
}
 
.product-price > .special-price, .product-price > .regular-price {
color: #111111;
}

And we are done! We have a nice-looking product grid:

Product Page

Another place to improve is the product page. We are aiming to have something like this:

Yet, we are starting with this:

Let's start with modifying the gallery:

resources/themes/laravelshop/views/products/view/gallery.blade.php

@inject ('wishListHelper', 'Webkul\Customer\Helpers\Wishlist')
 
@php
$images = product_image()->getGalleryImages($product);
 
$videos = product_video()->getVideos($product);
 
$images = array_merge($images, $videos);
@endphp
 
{!! view_render_event('bagisto.shop.products.view.gallery.before', ['product' => $product]) !!}
 
<div class="product-image-group">
<product-gallery></product-gallery>
</div>
 
{!! view_render_event('bagisto.shop.products.view.gallery.after', ['product' => $product]) !!}
 
@push('scripts')
<script type="text/x-template" id="product-gallery-template">
<li >
<img :src="currentLargeImageUrl" id="pro-img" :data-image="currentOriginalImageUrl" alt="" class="product-image"/>
</li>
</script>
 
<script>
let galleryImages = @json($images);
 
Vue.component('product-gallery', {
 
template: '#product-gallery-template',
 
data: function () {
return {
images: galleryImages,
 
thumbs: [],
 
currentLargeImageUrl: '',
 
currentOriginalImageUrl: '',
 
currentVideoUrl: '',
 
currentType: '',
 
counter: {
up: 0,
down: 0,
},
 
is_move: {
up: true,
down: true,
}
}
},
 
watch: {
'images': function (newVal, oldVal) {
this.changeImage(this.images[0]);
}
},
 
created: function () {
this.changeImage(this.images[0]);
 
},
 
methods: {
changeImage: function (image) {
this.currentType = image.type;
 
if (image.type == 'video') {
this.currentVideoUrl = image.video_url;
 
this.currentLargeImageUrl = image.large_image_url = image.video_url;
} else {
this.currentLargeImageUrl = image.large_image_url;
 
this.currentOriginalImageUrl = image.original_image_url;
}
 
if ($(window).width() > 580 && image.original_image_url) {
$('img#pro-img').data('zoom-image', image.original_image_url).ezPlus();
}
},
}
});
</script>
@endpush

Then we will modify the product page itself:

resources/themes/laravelshop/views/products/view.blade.php

@php use Illuminate\Support\Str; @endphp
 
@extends('shop::layouts.master')
 
@section('page_title')
{{ trim($product->meta_title) != "" ? $product->meta_title : $product->name }}
@stop
 
@section('seo')
<meta name="description"
content="{{ trim($product->meta_description) != "" ? $product->meta_description : Str::limit(strip_tags($product->description), 120, '') }}"/>
 
<meta name="keywords" content="{{ $product->meta_keywords }}"/>
 
@if (core()->getConfigData('catalog.rich_snippets.products.enable'))
<script type="application/ld+json">
{{ app('Webkul\Product\Helpers\SEO')->getProductJsonLd($product) }}
</script>
@endif
 
<?php $productBaseImage = product_image()->getProductBaseImage($product); ?>
 
<meta name="twitter:card" content="summary_large_image"/>
 
<meta name="twitter:title" content="{{ $product->name }}"/>
 
<meta name="twitter:description" content="{!! htmlspecialchars(trim(strip_tags($product->description))) !!}"/>
 
<meta name="twitter:image:alt" content=""/>
 
<meta name="twitter:image" content="{{ $productBaseImage['medium_image_url'] }}"/>
 
<meta property="og:type" content="og:product"/>
 
<meta property="og:title" content="{{ $product->name }}"/>
 
<meta property="og:image" content="{{ $productBaseImage['medium_image_url'] }}"/>
 
<meta property="og:description" content="{!! htmlspecialchars(trim(strip_tags($product->description))) !!}"/>
 
<meta property="og:url" content="{{ route('shop.productOrCategory.index', $product->url_key) }}"/>
@stop
 
@section('content-wrapper')
 
{!! view_render_event('bagisto.shop.products.view.before', ['product' => $product]) !!}
 
<section class="product-detail" style="background: #F3F3F3; padding-bottom: 64px; padding-top: 64px;">
 
<div class="layouter">
<product-view>
<div class="product-wrapper">
@csrf()
 
<input type="hidden" name="product_id" value="{{ $product->id }}">
<input type="hidden" name="quantity" value="1">
 
<div class="product-page">
<div class="product-page-headings">
 
<h1 class="product-title has-dash">{{ $product->name }}</h1>
<div class="product-price">
@include ('shop::products.price', ['product' => $product])
</div>
</div>
 
<div class="product-images">
<ul class="slides">
@include ('shop::products.view.gallery')
</ul>
</div>
 
<div class="product-details">
 
<div class="product-description">
{!! $product->description !!}
</div>
 
<div class="select">
@include ('shop::products.view.configurable-options')
</div>
 
<button class="button add-to-cart-button addtocart" type="submit"
data-add-title="Add to Cart" data-sold-title="Sold out">Add to Cart
</button>
</div>
</div>
</div>
</product-view>
</div>
</section>
 
{!! view_render_event('bagisto.shop.products.view.after', ['product' => $product]) !!}
@endsection
 
@push('scripts')
 
<script type="text/x-template" id="product-view-template">
<form method="POST" id="product-form" action="{{ route('shop.cart.add', $product->id) }}"
@click="onSubmit($event)">
 
<input type="hidden" name="is_buy_now" v-model="is_buy_now">
 
<slot></slot>
 
</form>
</script>
 
<script type="text/x-template" id="quantity-changer-template">
<div class="quantity control-group" :class="[errors.has(controlName) ? 'has-error' : '']">
<label class="required">{{ __('shop::app.products.quantity') }}</label>
 
<span class="quantity-container">
<button type="button" class="decrease" @click="decreaseQty()">-</button>
 
<input
ref="quantityChanger"
:name="controlName"
:model="qty"
class="control"
v-validate="validations"
data-vv-as="&quot;{{ __('shop::app.products.quantity') }}&quot;"
@keyup="setQty($event)">
 
<button type="button" class="increase" @click="increaseQty()">+</button>
</span>
 
<span class="control-error" v-if="errors.has(controlName)">@{{ errors.first(controlName) }}</span>
</div>
</script>
 
<script>
 
Vue.component('product-view', {
 
template: '#product-view-template',
 
inject: ['$validator'],
 
data: function () {
return {
is_buy_now: 0,
}
},
 
methods: {
onSubmit: function (e) {
if (e.target.getAttribute('type') != 'submit')
return;
 
e.preventDefault();
 
var this_this = this;
 
this.$validator.validateAll().then(function (result) {
if (result) {
this_this.is_buy_now = e.target.classList.contains('buynow') ? 1 : 0;
 
setTimeout(function () {
console.log(document.getElementById('product-form'))
document.getElementById('product-form').submit();
}, 0);
}
});
}
}
});
 
Vue.component('quantity-changer', {
template: '#quantity-changer-template',
 
inject: ['$validator'],
 
props: {
controlName: {
type: String,
default: 'quantity'
},
 
quantity: {
type: [Number, String],
default: 1
},
 
minQuantity: {
type: [Number, String],
default: 1
},
 
validations: {
type: String,
default: 'required|numeric|min_value:1'
}
},
 
data: function () {
return {
qty: this.quantity
}
},
 
mounted: function () {
this.$refs.quantityChanger.value = this.qty > this.minQuantity
? this.qty
: this.minQuantity;
},
 
watch: {
qty: function (val) {
this.$refs.quantityChanger.value = !isNaN(parseFloat(val)) ? val : 0;
 
this.qty = !isNaN(parseFloat(val)) ? this.qty : 0;
 
this.$emit('onQtyUpdated', this.qty);
 
this.$validator.validate();
}
},
 
methods: {
setQty: function ({target}) {
this.qty = parseInt(target.value);
},
 
decreaseQty: function () {
if (this.qty > this.minQuantity)
this.qty = parseInt(this.qty) - 1;
},
 
increaseQty: function () {
this.qty = parseInt(this.qty) + 1;
}
}
});
 
window.onload = function () {
var thumbList = document.getElementsByClassName('thumb-list')[0];
var thumbFrame = document.getElementsByClassName('thumb-frame');
var productHeroImage = document.getElementsByClassName('product-hero-image')[0];
 
if (thumbList && productHeroImage) {
 
for (let i = 0; i < thumbFrame.length; i++) {
thumbFrame[i].style.height = (productHeroImage.offsetHeight / 4) + "px";
thumbFrame[i].style.width = (productHeroImage.offsetHeight / 4) + "px";
}
 
if (screen.width > 720) {
thumbList.style.width = (productHeroImage.offsetHeight / 4) + "px";
thumbList.style.minWidth = (productHeroImage.offsetHeight / 4) + "px";
thumbList.style.height = productHeroImage.offsetHeight + "px";
}
}
 
window.onresize = function () {
if (thumbList && productHeroImage) {
 
for (let i = 0; i < thumbFrame.length; i++) {
thumbFrame[i].style.height = (productHeroImage.offsetHeight / 4) + "px";
thumbFrame[i].style.width = (productHeroImage.offsetHeight / 4) + "px";
}
 
if (screen.width > 720) {
thumbList.style.width = (productHeroImage.offsetHeight / 4) + "px";
thumbList.style.minWidth = (productHeroImage.offsetHeight / 4) + "px";
thumbList.style.height = productHeroImage.offsetHeight + "px";
}
}
}
};
</script>
@endpush

And, of course, add the required CSS:

public/themes/laravelshop/assets/css/shop.css

/* ... */
 
 
.product-page {
-webkit-text-size-adjust: 100%;
color: #111111;
font-family: "Varela Round", sans-serif;
font-size: 16px;
line-height: 1;
box-sizing: border-box;
padding-bottom: 30px;
position: relative;
}
 
.product-page {
padding-bottom: 30px;
position: relative;
}
 
.product-page:before, .product-page:after {
content: "";
display: table;
clear: both;
}
 
.product-page .product-page-headings {
float: left;
max-width: 35%;
padding-right: 40px;
padding-left: 0;
width: 100%;
}
 
.product-page .product-title {
-webkit-text-size-adjust: 100%;
font-family: "Varela Round", sans-serif;
box-sizing: border-box;
font-size: 2em;
color: #111111;
font-weight: normal;
line-height: 1.4em;
margin: 0;
padding: 0;
position: relative;
text-transform: uppercase;
letter-spacing: 0.5px;
margin-bottom: 24px;
text-align: center;
}
 
.product-page .product-price {
font-family: "Montserrat", sans-serif;
margin-bottom: 32px;
text-align: center;
font-size: 23px;
font-weight: normal;
line-height: 1.4em;
}
 
.product-wrapper {
-webkit-text-size-adjust: 100%;
color: #111111;
font-family: "Varela Round", sans-serif;
font-size: 16px;
line-height: 1;
box-sizing: border-box;
padding: 0 24px;
margin: 0 auto;
max-width: 1100px;
width: 100%;
}
 
h1.has-dash:after {
bottom: -8px;
content: "";
background-color: #111111;
height: 2px;
left: 50%;
position: absolute;
transform: translateX(-50%);
width: 24px;
}
 
.product-price {
-webkit-text-size-adjust: 100%;
color: #111111;
box-sizing: border-box;
font-family: "Montserrat", sans-serif;
margin-bottom: 32px;
text-align: center;
font-size: 23px;
font-weight: normal;
line-height: 1.4em;
}
 
.product-images {
-webkit-text-size-adjust: 100%;
color: #111111;
font-family: "Varela Round", sans-serif;
font-size: 16px;
line-height: 1;
box-sizing: border-box;
float: right;
max-width: 63%;
padding-left: 24px;
padding-right: 0;
position: relative;
width: 100%;
}
 
.product-images .slides {
-webkit-text-size-adjust: 100%;
color: #111111;
font-family: "Varela Round", sans-serif;
font-size: 16px;
line-height: 1;
box-sizing: border-box;
margin: 0;
padding: 0;
list-style: none;
}
 
.product-images .slides li {
-webkit-text-size-adjust: 100%;
color: #111111;
font-family: "Varela Round", sans-serif;
font-size: 16px;
line-height: 1;
box-sizing: border-box;
margin: 0;
padding: 0;
list-style: none;
}
 
.product-image {
-webkit-text-size-adjust: 100%;
color: #111111;
font-family: "Varela Round", sans-serif;
font-size: 16px;
line-height: 1;
list-style: none;
box-sizing: border-box;
border-style: none;
border-radius: 4px;
display: block;
margin-bottom: 32px;
max-width: 100%;
width: 100%;
}
 
.product-details {
-webkit-text-size-adjust: 100%;
color: #111111;
font-family: "Varela Round", sans-serif;
font-size: 16px;
line-height: 1;
box-sizing: border-box;
clear: left;
float: left;
max-width: 35%;
padding-right: 24px;
padding-left: 0;
width: 100%;
margin-bottom: 0;
}
 
.product-description {
-webkit-text-size-adjust: 100%;
color: #111111;
box-sizing: border-box;
font-family: "Montserrat", sans-serif;
font-size: 16px;
line-height: 1.5em;
margin-bottom: 32px;
}
 
.add-to-cart-button {
-webkit-text-size-adjust: 100%;
box-sizing: border-box;
overflow: visible;
margin: 0;
cursor: pointer;
border: none;
border-radius: 4px;
font-family: "Varela Round", sans-serif;
transition: color, background-color 0.1s linear;
background: #000000;
color: #FFFFFF;
font-size: 15px;
height: 58px;
line-height: 58px;
max-width: 100%;
padding: 0 16px;
text-align: center;
text-transform: uppercase;
letter-spacing: 0.5px;
-webkit-appearance: button;
display: block;
width: 100%;
}
 
.control-group .control {
-webkit-text-size-adjust: 100%;
box-sizing: border-box;
font-size: 100%;
text-transform: none;
cursor: pointer;
appearance: none;
background: none;
border-radius: 4px;
color: #111111;
font-family: "Varela Round", sans-serif;
line-height: normal;
padding: 0 66px 0 16px;
position: relative;
width: 100%;
z-index: 2;
border: 2px solid #111111;
height: 58px;
margin-bottom: 16px;
max-width: 100%;
}
 
.attributes label {
display: none;
}

Once we are done, we will have a product page that looks like this:

And while we can agree that it's not perfect - it's close enough and displays how easy it is to customize Bagisto to your liking. If you are following this process - take a look at the following steps:

  • Changing Cart Page - resources/themes/laravelshop/views/checkout/cart/index.blade.php
  • Changing Checkout Page - resources/themes/laravelshop/views/checkout/onepage.blade.php
  • Modifying Payment Page - resources/themes/laravelshop/views/checkout/onepage/payment.blade.php
  • Modifying Order Review Page - resources/themes/laravelshop/views/checkout/onepage/review.blade.php
  • Modifying Shipping Page - resources/themes/laravelshop/views/checkout/onepage/shipping.blade.php
  • Modifying Total Page - resources/themes/laravelshop/views/checkout/onepage/total.blade.php
  • And so on...

You should have an idea of how things are structured and where you can find the files you need to modify. You may want to reference Controllers or modify them too. For example, if you're going to modify the Cart Controller, you can find it here: packages/Webkul/Checkout/src/Http/Controllers/CartController.php.


Final Thoughts

As we all know, there's way more to creating themes than what we just did. For example, we haven't touched the Cart or Checkout systems.

But with this tutorial, we aimed to show you how easy it is to start creating themes for Bagisto. And this is, of course, only some of the pages. Creating an entire theme would require a lot more checks:

  • Did we miss any pages?
  • Is the entire flow and links working?
  • Are all the pages responsive?
  • Are all the pages accessible?
  • Does everything work on all browsers?
  • Is there any functionality that needs to be added? For example, we might have missed some color switchers or something like that.

So these can be your "homework", as you now understand how Bagisto themes work.

If you want more tutorials on Bagisto, including the cart/checkout processes, let us know in the comments, and we will consider it in the future.