Browsed by
Category: Portfolio

Building a new Moodle Theme

Building a new Moodle Theme

With the launch of the college’s new branding and a planned Moodle upgrade our Moodle theme needed a refresh. Over the last 6 years or so I’ve built numerous WordPress themes from scratch so I took on a new challenge and looked at the Moodle Documentation and began learning how to building our own using the college’s new colours and fonts.

This was entirely new to me as I only started picking up Moodle when I joined the College in December 2019 and had to pick it up rather quickly given the pandemic.

Design Principals

There were a few things I was keeping in mind.

  • To maximise the space as much as possible for the Lecturers and Students
  • To make everything as accessible as possible.
  • Keep it as simple as possible ( K.I.S.S. )

Accessibility Guide
WCAG Accessibility Guidelines
The Public Sector Bodies (Websites and Mobile Applications) (No. 2) Accessibility Regulations 2018

Problems to overcome

There were (and still are) a few barriers to building the theme. Some of these will disappear once we are able to move to the new course layout that my colleague Steve has designed and developed. For now we need to support the legacy setup.

  • Multiple course formats have been installed into the existing VLE and need to be supported.
    I focused on the CORE functionality to start with then started working on adding in support as we worked out what course formats were still being used.
  • Multiple plugins have been installed into the existing VLE over the years and some need to be supported.
    This caused unexpected conflicts at points that took time to diagnose.
    Eventually our plan is to install a fresh Moodle with Steve’s course layout and we will go through and remove unnecessary plugins

Overview of the new design.

To break this very long post up I have separated out each component into a separate post. Some of the changes were simple changes to the SCSS files and others involved output renderer overrides. Click on each of the post embeds to find out more.

Screenshot of the new Moodle Theme.
Screenshot of the New Moodle Theme Dashboard

Navigation Panel

Role Descriptor on top Menu

You are logged in as….

Redesigning the footer…

Currently writing up

  • User Menu
  • Custom Menu to useful services for the Students.

Editor Button

Currently writing up…

Forum Post Layout

Currently writing up

Course Search Layout

Currently writing up

Book Navigation Modifications

Currently writing up

POST TO BE CONTINUED….

Useful Documentation

Creating a Theme using Boost (Moodle Documentation)
Themes Overview (Moodle Documentation)
Moodle Template Overview
Moodle Renderers
Moodle Output Renderers
Moodle Filters
Moodle Roles
Moodle Navigation API
Useful UnObvious things
How to Override a Template
How to Override a renderer

Mustache Documentation
Moodle Theme SCSS
Moodle Theme Pre and Post SCSS
Moodle HTML Writer

Moodle Data Manipulation API


Moodle Theme : Modifying the Navigation Panel

Moodle Theme : Modifying the Navigation Panel

This post is part of a series documenting the work I did while developing a new Moodle Theme for Dumfries and Galloway College’s VLE. There will be further posts to follow. Watch this space!

Modifying the navigation panel was a fairly small change from the base boost theme this theme was based on. The main changes made were :

  • Animating the menu toggle button along the top using the excellent CSS library by Jonathan Sue.
    Note : rather than include the entire library I only included the SCSS being used.
  • Modifying the side panel so when minimised you could still see and click on the icons.
  • Modifying how the scrollbar displayed

The side panel can be navigated easily by keyboard (this is part of Moodle Core so I didn’t need to add this in). It is also scrollable on small screens or when there’s a lot of content.

The main content re-adjusts as the side panel is opened and closed (see example in the second video). This is actually handled using CSS and media queries targeting the classes added by Moodle when the side panel is open or closed. I’ve added some example SCSS below.

.courses  { 
	grid-template-columns: 1fr 1fr;
	nav,
	.paging-showall {
		grid-column-start: 1;
		grid-column-end: 3;
	}
	.drawer-open-left & {
		grid-template-columns: 1fr;
		nav,
		.paging-showall {
			grid-column-end: 2;
		}
	}
}

Handling Feedback and Adding in a Scroll Indicator

Initially I set the scrollbar to be invisible. I did this because on Windows machines the scrollbar is large, chunky and takes up a fair bit of space on the side panel. It also doesn’t look very aesthetically pleasing compared to the Mac OS scrollbar. We were also keen to maximise space for the learner where we could so I felt hiding it while keeping the area scrollable as you hovered over this area was a good choice.

Despite the scrollbar being invisible it was still easy to scroll the side panel by gesture (mobile devices) or by using the mouse scroll wheel when hovered over and as mentioned above it was possible to navigate via keyboard for accessibility.

However, thanks to feedback from one of our Lecturers Margaret, it became clear that on some devices which didn’t have good trackpad / scroll support the scrolling feature wasn’t working quite so well. It took a bit of back and forth to isolate the issue as every device I tested on windows or mac worked correctly and I’m very grateful for Margaret’s patience!

After some additional research into the issue and discovering the cause could be due to the trackpad support on certain devices I felt we needed to have some kind of indicator that could be clicked and dragged to :

  • a) solve the issue Margaret had flagged
  • b) to improve on the existing accessibility by giving a visual cue.
  • c) make it more obvious that the section was scrollable

I also wanted to make sure that the scroll indicator didn’t take up too much space in the relatively small area and was consistent across devices and browsers.

For both methods (making invisible and changing the styling) I used CSS selectors that targeted the Shadow DOM.

The first code block is the original hidden scrollbar styling that uses the CSS selectors for the shadow DOM. There is some extra code to add support for old versions of IE (no longer an issue) and Firefox.

.scrollbar-hidden::-webkit-scrollbar {
	display: none;
}

/* Hide scrollbar for IE, Edge add Firefox */
.scrollbar-hidden {
	-ms-overflow-style: none;
	scrollbar-width: none; /* Firefox */
}

The second code block is the updated shadow DOM styling which just adjusts the look of the scrollbar so it is thinner but visible and matches the brand colours.

.scrollbar-thin::-webkit-scrollbar {
	width: 5px;
	height: 4px;
	cursor: pointer;
}
.scrollbar-thin::-webkit-scrollbar-thumb{
	background: $DGCOrange;
	border-radius: 0px;
}

.scrollbar-thin::-webkit-scrollbar-thumb:hover{
	background: $OrangeTint75;
	cursor: pointer;
}
.scrollbar-thin::-webkit-scrollbar-track{
	background: transparent;
	border-radius: 0px;
	box-shadow: inset 0px 0px 0px 0px #F0F0F0;
}

Further Reading on Styling Scrollbars using the Shadow DOM

There’s an excellent guide on CSS Tricks about using the shadow Dom to customise scroll bars linked below.

https://css-tricks.com/custom-scrollbars-in-webkit/

Further Improvements?

Web development is of course never done and it’s important to constantly think about how we can continue to improve the interface for users. There are a couple of things I’m thinking about in order to improve and develop this further.

  • A “hover over” to show the menu item title when it’s hovered over when collapsed. This does sort of happen anyway if you hover for long enough but I think it would be a nice feature if it was more instantaneous.
  • Checking that everything responds to the view area shrinking / growing – there may be a few areas that have been missed since Moodle is so huge and we have multiple extra course formats installed as well as hundreds of extra plugins.
  • Work out what changes are needed for Moodle 4.

I hope to add these in future iterations of the theme or in my Moodle 4 upgrade.

Breadcrumbs CSS

Breadcrumbs CSS

Breadcrumb Styling example I wrote.

I need to double check browser support. Add a fallback for IE 11 and add mobile support.

YouTube OpenDay Live Feed

YouTube OpenDay Live Feed

Due to the unprecedented circumstances this year the College had to host their Open Day virtually.

We wanted to have a live feed of heads of departments answering questions which we got during the feed and beforehand. Because we wanted this front and centre we had to look into a way of embedding the feed into our dedicated Open Day webpage.

After looking around online I came across this interesting discussion on embedding Live YouTube Feeds and used it as a basis to create my own class.

https://stackoverflow.com/questions/44354421/how-to-embed-youtube-live-chat-with-url-permanent

<?php 
/*****************************************************************
	Class : LIVE EVENT SCHEDULER 
	
	This class handles the live event schedule and loads the 
	youtube live feed if an event is due.
	
	FUNCTIONS
	---------------------------------------------------------
	1. showAll()
		show all scheduled events as cards below the main feed
	2. getNextEvent()
		get an array of the next event's details
	3. showAllUpcoming()
		display a mini feed of all the upcoming events.
	4. showAllPast()
	5. getLiveChatURL()
	6. getLiveVideoID($channelId)
	7. fetchVideoFeeds()
	
*****************************************************************/
class Schedule {
	
	public $schedule = array(
	    array(
			'datetime'	=> '202006121100',
			'show'		=> false, // show in main schedule
			'subject'	=> 'Welcome Message',
			'speaker'	=> 'Principal & Vice Principal ',
			'img'		=> '',
			'vid'		=> '',
			'desc'		=> 'Welcome message from the Principal.',
			'link'		=> array( 
						array(	'link' => '', 						'text' => 'Find out more' )),
		),

	);
	
	// Properties
	public $date;
	public $time;
	public $speaker;
	public $subject;
	
	function __construct() {
		
	}
	
	/****************************************
	show all upcoming scheduled items. 
	These appear as cards below the main feed.
	
	@RETURNS
	$html (string) 	: Compiled HTML
	
	****************************************/
	public function showAll() {
		
                // Display code goes here.....
		
		return $html;
	}
	
	/*********************************************
		
		Get the next event
		This was used to help calculate when the next event was.
		
		@RETURNS
		$return (array)	: The event array that is next up.
		
	*********************************************/
	public function getNextEvent() {
		$now 	= date_create(); // get current date and time. 
		$compare 	= date_format($now,"YmdHis"); // formatted current date and time to use as a comparison
		$return 	= $this->schedule[0]; // The event array that is next up. (default as first)
		
		// loop through events and compare with formatted date. 
		foreach($this->schedule as $event) {
			
			if($compare < $event['datetime'].'00') {
				$return = $event;
				break;
			}
		}
		
		return $return; 
	} 
	
	/***************************************************
		Show all upcoming events as a mini feed
		
		@RETURNS
		$html (string) : Container for the compiled HTML
	***************************************************/
	public function showAllUpcoming() {

		// Display code goes here ....
		
		return $html;
	}
	
	/***********************************************
		Show all past events
		added so that past videos could be viewed.
		
		@RETURNS
		$html (string) : Compiled HTML of past events. 
		
	***********************************************/
	public function showAllPast() {
		// Display Code goes here....
		
		return $html;
	}
	
	/********************************************
		Fetch Live Chat URL from YouTube Channel
		This requires a YouTube Channel ID.
		
		It will try to fetch the current live video feed ID 
		present on that Youtube Channel. 
		If it can't find any Live Feeds will return an error. 
		If it can the live chat feed url is returned. 
	*********************************************/
	public function getLiveChatURL() {
			
		try {
		    $livevideoId = $this->getLiveVideoID('<< REPLACE WITH CHANNEL ID >>');
		
		    // Output the Chat URL
		    //echo "The Chat URL is https://www.youtube.com/live_chat?v=".$livevideoId;
		    return array( true, "https://www.youtube.com/live_chat?v=".$livevideoId);
		} catch(Exception $e) {
			
			
		    // Echo the generated error
		    return array( false, "1. ERROR: ".$e->getMessage());
		}
	}
	
	// The method which finds the video ID
	public function getLiveVideoID($channelId)
	{
	    $livevideoId = null;
	
	    // Fetch the livestream page
	    if($data = file_get_contents('https://www.youtube.com/embed/live_stream?channel='.$channelId))
	    {
	        // Find the video ID in there
	        if(preg_match('/\'VIDEO_ID\': \"(.*?)\"/', $data, $matches))
	            $videoId = $matches[1];
	        else
	            throw new Exception('2. Couldn\'t find video ID');
	    }
	    else
	        throw new Exception('3. Couldn\'t fetch data');
	
	    return $videoId;
	}
	
	// AJax function.
	public function fetchVideoFeeds() {
		$html = '';
		$result = $this->getLiveChatURL();
		
		$html .= '<div class="col-xs-12 col-lg-8">';
					//$html .= '<pre>Dev Note : We are testing the live stream right now</pre><iframe style="width: 100%; height: 411px;" src="https://www.youtube.com/embed/live_stream?channel=CHANNEL_ID_HERE&autoplay=1&rel=0&showinfo=0&cc_load_policy=1" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen autoplay></iframe>';

					if($result[0] == true) {
						$html .= '<iframe title="VIDEO FEED TITLE" aria-label="VIDEO ARIA LABEL" style="width: 100%; height: 411px;" src="https://www.youtube.com/embed/live_stream?channel=CHANNEL_ID_HERE&autoplay=1&rel=0&showinfo=0&cc_load_policy=1" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen autoplay></iframe>';
					} else {
						$html .= '<iframe title="VIDEO TITLE" aria-label="VIDEO ARIA LABEL" style="width: 100%; height: 411px;" src="https://www.youtube-nocookie.com/embed/ZvzgjA5hhdo?rel=0&autoplay=0&rel=0&showinfo=0&cc_load_policy=1" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen></iframe>';
					} 
				$html .= '</div>
				<div class="col-xs-12 col-lg-4">';
				
					if($result[0] == true) {
						$html .= '<iframe src="'.$result[1].'&embed_domain=www.YOURDOMAIN.ac.uk" style="width: 100%; height: 411px" frameborder="0"></iframe>';
					} else {
						$html .= '<div class="nofeed">';
							$html .= '<h2 class="arrow">Upcoming Events </h2>';
							$nextEvent = $this->getNextEvent();
							$datetime = date_create($nextEvent['datetime']);
							$today = date_create();
							$dateDiff = date_diff($today,$datetime);
							
							$html .= '<div class="content">';
								$html .= '<div class="countdown">';
									$html .= '<div style="display: none;">'.($dateDiff->d > 0 ? $dateDiff->d.'d ' : '').(($dateDiff->d > 0 || $dateDiff->h) > 0 ? $dateDiff->h.'h ' : ''). (($dateDiff->h || $dateDiff->i > 0) ? $dateDiff->i.'m ' : '').$dateDiff->s.'s</div>';
									$html .= '<h6>The next event will be in <time class="counter" datetime="'.date_format($datetime,'Y-m-d\TH:i:s\Z').'" data-now="'.date_format($today, 'Y-m-d\TH:i:s\Z').'">'.($dateDiff->d > 0 ? $dateDiff->d.'d ' : '').(($dateDiff->d > 0 || $dateDiff->h) > 0 ? $dateDiff->h.'h ' : ''). (($dateDiff->h || $dateDiff->i > 0) ? $dateDiff->i.'m ' : '').$dateDiff->s.'s</time></h6>';
									$html .= $this->showAllUpcoming(); 
																		
								$html .= '</div>'; 
								
							$html .= '</div>';
						$html .= '</div>';
					} 
				$html .= '</div>';
				
		return $html;
	}
}

// init
$schedule = new Schedule(); 
?>

(I’ve taken out some of the irrelevant display bits).

The Schedule

For this example I have the schedule as an array but really it would be better to use a database and add some fetch from database functions to the class.

showAll(), showAllUpcoming(),showAllPast()

These functions just displayed all the upcoming events below the feed and aren’t important for this post.

getNextEvent()

We had a little countdown running on the page to the next event this function was just to help with that countdown and not important for this post.

getLiveChatURL()

This function tries to fetch the Live Chat url if it fails it will return an error message.

$livevideoId = $this->getLiveVideoID('PASTE CHANNEL URL HERE');

To get the Live Chat URL we also need the Live Video ID. This is fetched via the next function getLiveVideoID() . It requires the Channel ID to get this.

Getting the Channel ID

It’s actually fairly straightforward to get the Channel ID. You can either get this from your channel settings or you can simply go to your channel and the ID will display at the end of your URL.

Screenshot of YouTube Channel URL - the ID is the string of characters after /channel/
the ID is the string of characters after /channel/

Note About YouTube Requirements

Please note there are a few requirements regarding embedding Live Video Feeds.

  1. To stream direct to Youtube feeds must be public and allow embedding for the script to work.
  2. You need a certain number of subscribers (1000+) and be eligible for the YouTube Partner Program to be able to embed youtube feeds on your websites directly.
  3. Had we known! Restream.io costs but you can go live on multiple platforms at once (Twitter/Twitch/Facebook/Instagram) and embed on any website you like. ( thanks Steve 🙂 )
return array( true, "https://www.youtube.com/live_chat?v=".$livevideoId);

The Live Chat Feed for any Live Video will follow the above format. Of course this might change in the future with YouTube updates but this works at time of writing.

/********************************************
		Fetch Live Chat URL from YouTube Channel
		This requires a YouTube Channel ID.
		
		It will try to fetch the current live video feed ID 
		present on that Youtube Channel. 
		If it can't find any Live Feeds will return an error. 
		If it can the live chat feed url is returned. 
	*********************************************/
	public function getLiveChatURL() {
			
		try {
		    $livevideoId = $this->getLiveVideoID('PASTE CHANNEL ID HERE');
		
		    // Output the Chat URL
		    //echo "The Chat URL is https://www.youtube.com/live_chat?v=".$livevideoId;
		    return array( true, "https://www.youtube.com/live_chat?v=".$livevideoId);
		} catch(Exception $e) {
			
			
		    // Echo the generated error
		    return array( false, "1. ERROR: ".$e->getMessage());
		}
	}

getLiveVideoID($channelId)

This function gets the ID of the Live Video using the provided Channel ID.

if($data = file_get_contents('https://www.youtube.com/embed/live_stream?channel='.$channelId))

Currently the Stream url will follow the above format. Of course this might change in the future with YouTube updated but this works at time of writing.

if(preg_match('/\'VIDEO_ID\': \"(.*?)\"/', $data, $matches))
	$videoId = $matches[1];
else
	 throw new Exception('2. Couldn\'t find video ID');

The script then searches the returned file contents for the ‘VIDEO_ID’ variable which is printed in the javaScript on that url.

^ There may be another way of doing this via YouTube’s Javascript Api but this does work.

// The method which finds the video ID
	public function getLiveVideoID($channelId)
	{
	    $livevideoId = null;
	
	    // Fetch the livestream page
	    if($data = file_get_contents('https://www.youtube.com/embed/live_stream?channel='.$channelId))
	    {
	        // Find the video ID in there
	        if(preg_match('/\'VIDEO_ID\': \"(.*?)\"/', $data, $matches))
	            $videoId = $matches[1];
	        else
	            throw new Exception('2. Couldn\'t find video ID');
	    }
	    else
	        throw new Exception('3. Couldn\'t fetch data');
	
	    return $videoId;
	}

fetchVideoFeeds()

Finally I added a function to display the final feed or schedule if there was no live feed. This can also be requested via Ajax to update the feed automatically once the countdown has been reached.

$result = $this->getLiveChatURL();

First, this function gets the current Live Chat result from the above function. If there’s no result from this e.g. an error the script instead shows an advert for the college and a schedule of upcoming Live Events.

Screenshot of the alternative video and schedule of live events.
Screenshot of the upcoming Events Schedule.

If there is a result the script replaces the video advert with the live feed…

if($result[0] == true) {
	$html .= '<iframe title="VIDEO TITLE HERE" aria-label="VIDEO ARIA LABEL" style="width: 100%; height: 411px;" src="https://www.youtube.com/embed/live_stream?channel=CHANNEL_ID_HERE&autoplay=1&rel=0&showinfo=0&cc_load_policy=1" frameborder="0" allow="accelerometer; autoplay; encrypted-media; gyroscope; picture-in-picture" allowfullscreen autoplay></iframe>';
}

Note : I should probably update this so that the Live Stream url is inserted by the schedule class.

if($result[0] == true) {
	$html .= '<iframe src="'.$result[1].'&embed_domain=www.dumgal.ac.uk" style="width: 100%; height: 411px" frameborder="0"></iframe>';
}

It also adds in the Live Chat embed. Note the &embed_domain variable. This should be set to your websites url otherwise the video will not show.

$nextEvent = $this->getNextEvent();
							$datetime = date_create($nextEvent['datetime']);
							$today = date_create();
							$dateDiff = date_diff($today,$datetime);
							
							$html .= '<div class="content">';
								$html .= '<div class="countdown">';
									$html .= '<div style="display: none;">'.($dateDiff->d > 0 ? $dateDiff->d.'d ' : '').(($dateDiff->d > 0 || $dateDiff->h) > 0 ? $dateDiff->h.'h ' : ''). (($dateDiff->h || $dateDiff->i > 0) ? $dateDiff->i.'m ' : '').$dateDiff->s.'s</div>';
									$html .= '<h6>The next event will be in <time class="counter" datetime="'.date_format($datetime,'Y-m-d\TH:i:s\Z').'" data-now="'.date_format($today, 'Y-m-d\TH:i:s\Z').'">'.($dateDiff->d > 0 ? $dateDiff->d.'d ' : '').(($dateDiff->d > 0 || $dateDiff->h) > 0 ? $dateDiff->h.'h ' : ''). (($dateDiff->h || $dateDiff->i > 0) ? $dateDiff->i.'m ' : '').$dateDiff->s.'s</time></h6>';
									
									$html .= $this->showAllUpcoming(); 

I also added in a little countdown which is updated by javaScript.

All this html is then returned by the function.

Initialising the Class

// init
$schedule = new Schedule(); 

Final part of the class.php file it creates a new instance of the class.

The Open Day Page

On the open day page where the feed is to be displayed I included the new class file.

<?php 
			
// Include the Event Scheduler Class. (handles the schedules and live video links)
include('classes/class_schedule.php');
	
?>	

And added in an area for the Live Feed to go…

<!-- LIVE VIDEO FEEDS & CHAT -->
<a class="pastevents" href="#eventspast" title="View Past Events" aria-label="View Past Events">Watch Past Events</a>
<div id="videofeed" class="row videofeeds">
	<?php echo $schedule->fetchVideoFeeds(); ?>
</div>
<!-- END OF LIVE VIDEO FEEDS AND CHAT -->

JavaScript Countdown

I also added in a little bit of javaScript that updates the countdown and reloads the feed area once the countdown hits 0.

jQuery(document).ready( function($) {
	
	// Set the date we're counting down to
	var nextEventDate = $('.counter').attr('datetime');
	var today = $('.counter').data('now');
	var countDownDate = new Date(nextEventDate).getTime(); 
	
	// Update the count down every 1 second
	var x = setInterval(function() {
	
	  // Get today's date and time
	  var today = new Date();
	  // DST
	  today.setHours(today.getHours() + 1);
	  
	  var now = today.getTime();
	
	  // Find the distance between now and the count down date
	  var distance = countDownDate - now;
	
	  // Time calculations for days, hours, minutes and seconds
	  var days = Math.floor(distance / (1000 * 60 * 60 * 24));
	  var hours = Math.floor((distance % (1000 * 60 * 60 * 24)) / (1000 * 60 * 60));
	  var minutes = Math.floor((distance % (1000 * 60 * 60)) / (1000 * 60));
	  var seconds = Math.floor((distance % (1000 * 60)) / 1000);
	
	  // Display the result in the element with id="demo"
	  $('.counter').html((days > 0 ? days + "d " : '') + ((days > 0 || hours > 0) ? hours + "h " : '') + ((hours > 0 || minutes > 0) ? minutes + "m " : '') + seconds + "s ");
	
	  // If the count down is finished, write some text
	  if (distance < 0) {
	    clearInterval(x);
	    $('.counter').html('Now....reloading in <span class="reloadcount">3</span>');
			    
		// fetch the feed...
		$.get("fetch.php", function(data, status){
			$('.videofeeds').html(data); //.animate({'opacity' : '1'}); // fade back in. 
		});  
	  }
	}, 1000);    //testing    
	
});

Notes about Feed Refresh

There is roughly a minute delay between when you start your Live Feed and it appearing on YouTube so it’s a good idea to have a buffer time and start the Live Feed before the event is scheduled. This also mitigates any issues with server times being slightly out of sync.

We had a 15 minute buffer period before each event.

fetch.php

The fetch.php file just includes the schedule class and echos out the feed.

<?php 		
// Include the Event Scheduler Class. (handles the schedules and live video links)
include('classes/class_schedule.php');

echo $schedule->fetchVideoFeeds();

?>
Useful Shell Scripts

Useful Shell Scripts

I wrote a couple of Shell Scripts to save time when splitting SCSS into breakpoint files.

https://github.com/dgrumbler/useful_shell_scripts

$ sh sassBreakpoints.sh

This creates a named folder based on the name of the module you type in.
It then creates all the breakpoints :

  • _base.scss
  • _481up.scss
  • _768down.scss
  • _768up.scss
  • _899down.scss
  • _900up.scss
  • _1029down.scss
  • _1030up.scss
  • _1240up.scss
  • _1400up.scss
  • _1600up.scss
  • _1900up.scss

Then for each breakpoint it adds the following:

/****************************************************************
	Theme: << Module Name >>
	Viewport: << Viewport Size >>
	Author: << Author >>
****************************************************************/

Replacing Module name, viewport size and author with the user input and filename.

e.g.

/****************************************************************
	Theme: Carousel
	Viewport: 768 up
	Author: Rebecca Rumble
****************************************************************/

This one does the same as the above except it doesn’t create a folder and runs in the current directory.

$ sh sassBreakpointsNoFolder.sh