Introduction
I decided to develop a theme for realtors in my city as A LOT of realtors in my city are still using wordpress and I wanted to create an alternative for them.
The Plan
Here is the game plan:
- Find an SSG that will work
- Build out the pages first in HTML, SASS and JS
- Build out the templates
- Find a CMS that will work with the themes and SSG
For this article, I am going to cover the SSG and Pages
The SSG
For the static site generator, I decided to go with Hugo. It is simple to use and the templating engine is awesome! After reading the docs I think it will work with what I need.
Building out the pages
So, the main pages I require are:
- Home
- Listings
- A listing
- Staff Page
- Contact Us
Home Page
Listings
For the listing page I will need to set up a filterable gallery.
The Gallery
I decided to go witht thise style of gallery I think it looks nice. Next to add some filtering
The filters
// File#: _3_advanced-filter // Usage: codyhouse.co/license (function() { // the AdvFilter object is used to handle: // - number of results // - form reset // - filtering sections label (to show a preview of the option selected by the users) var AdvFilter = function(element) { this.element = element; this.form = this.element.getElementsByClassName('js-adv-filter__form'); this.resultsList = this.element.getElementsByClassName('js-adv-filter__gallery')[0]; this.resultsCount = this.element.getElementsByClassName('js-adv-filter__results-count'); initAdvFilter(this); }; function initAdvFilter(filter) { if(filter.form.length > 0) { // reset form filter.form[0].addEventListener('reset', function(event){ setTimeout(function(){ resetFilters(filter); resetGallery(filter); }); }); // update section labels on form change filter.form[0].addEventListener('change', function(event){ var section = event.target.closest('.js-adv-filter__item'); if(section) resetSelection(filter, section); else if( Util.is(event.target, '.js-adv-filter__form') ) { // reset the entire form lables var sections = filter.form[0].getElementsByClassName('js-adv-filter__item'); for(var i = 0; i < sections.length; i++) resetSelection(filter, sections[i]); } }); } // reset results count if(filter.resultsCount.length > 0) { filter.resultsList.addEventListener('filter-selection-updated', function(event){ updateResultsCount(filter); }); } }; function resetFilters(filter) { // check if there are custom form elemets - reset appearance // custom select var customSelect = filter.element.getElementsByClassName('js-select'); if(customSelect.length > 0) { for(var i = 0; i < customSelect.length; i++) customSelect[i].dispatchEvent(new CustomEvent('select-updated')); } // custom slider var customSlider = filter.element.getElementsByClassName('js-slider'); if(customSlider.length > 0) { for(var i = 0; i < customSlider.length; i++) customSlider[i].dispatchEvent(new CustomEvent('slider-updated')); } }; function resetSelection(filter, section) { // change label value based on input types var labelSelection = section.getElementsByClassName('js-adv-filter__selection'); if(labelSelection.length == 0) return; // select var select = section.getElementsByTagName('select'); if(select.length > 0) { labelSelection[0].textContent = getSelectLabel(section, select[0]); return; } // input number var number = section.querySelectorAll('input[type="number"]'); if(number.length > 0) { labelSelection[0].textContent = getNumberLabel(section, number); return; } // input range var slider = section.querySelectorAll('input[type="range"]'); if(slider.length > 0) { labelSelection[0].textContent = getSliderLabel(section, slider); return; } // radio/checkboxes var radio = section.querySelectorAll('input[type="radio"]'), checkbox = section.querySelectorAll('input[type="checkbox"]'); if(radio.length > 0) { labelSelection[0].textContent = getInputListLabel(section, radio); return; } else if(checkbox.length > 0) { labelSelection[0].textContent = getInputListLabel(section, checkbox); return; } }; function getSelectLabel(section, select) { if(select.multiple) { var label = '', counter = 0; for (var i = 0; i < select.options.length; i++) { if(select.options[i].selected) { label = label + '' + select.options[i].text; counter = counter + 1; } if(counter > 1) label = section.getAttribute('data-multi-select-text').replace('{n}', counter); } return label; } else { return select.options[select.selectedIndex].text; } }; function getNumberLabel(section, number) { var counter = 0; for(var i = 0; i < number.length; i++) { if(number[i].value != number[i].min) counter = counter + 1; } if(number.length > 1) { // multiple input number in this section if(counter > 0) { return section.getAttribute('data-multi-select-text').replace('{n}', counter); } else { return section.getAttribute('data-default-text'); } } else { if(number[0].value == number[0].min) return section.getAttribute('data-default-text'); else return section.getAttribute('data-number-format').replace('{n}', number[0].value); } }; function getSliderLabel(section, slider) { var label = '', labelFormat = section.getAttribute('data-number-format'); for(var i = 0; i < slider.length; i++) { if(i != 0 ) label = label+' - '; label = label + labelFormat.replace('{n}', slider[i].value); } return label; }; function getInputListLabel(section, inputs) { var counter = 0; label = ''; for(var i = 0; i < inputs.length; i++) { if(inputs[i].checked) { var labelElement = inputs[i].parentNode.getElementsByTagName('label'); if(labelElement.length > 0) label = labelElement[0].textContent; counter = counter + 1; } } if(counter > 1) return section.getAttribute('data-multi-select-text').replace('{n}', counter); else if(counter == 0 ) return section.getAttribute('data-default-text'); else return label; }; function resetGallery(filter) { // emit change event + reset filtering filter.form[0].dispatchEvent(new CustomEvent('change')); filter.resultsList.dispatchEvent(new CustomEvent('update-filter-results')); }; function updateResultsCount(filter) { var resultItems = filter.resultsList.children, counter = 0; for(var i = 0; i < resultItems.length; i++) { if(isVisible(resultItems[i])) counter = counter + 1; } filter.resultsCount[0].textContent = counter; }; function isVisible(element) { return (element.offsetWidth || element.offsetHeight || element.getClientRects().length); }; //initialize the AdvFilter objects var advFilter = document.getElementsByClassName('js-adv-filter'); if( advFilter.length > 0 ) { for( var i = 0; i < advFilter.length; i++) { (function(i){new AdvFilter(advFilter[i]);})(i); } } // Remove the code below if you want to use a custom filtering function (e.g., you need to fetch your results from a database) // The code below is used for filtering of page content (animation of DOM elements, no fetching results from database). // It uses the Filter component (https://codyhouse.co/ds/components/app/filter) - you can modify the custom filtering functions based on your needs // Check the info page of the component for info on how to use it: https://codyhouse.co/ds/components/info/filter var gallery = document.getElementById('adv-filter-gallery'); if(gallery) { new Filter({ element: gallery, // this is your gallery element priceRange: function(items){ // this is the price custom function var filteredArray = [], minVal = document.getElementById('slider-min-value').value, maxVal = document.getElementById('slider-max-value').value; for(var i = 0; i < items.length; i++) { var price = parseInt(items[i].getAttribute('data-price')); filteredArray[i] = (price >= minVal) && (price <= maxVal); } return filteredArray; }, indexValue: function(items){ // this is the index custom function var filteredArray = [], value = document.getElementById('index-value').value; for(var i = 0; i < items.length; i++) { var index = parseInt(items[i].getAttribute('data-sort-index')); filteredArray[i] = index >= value; } return filteredArray; } }); } }()); A Listing
Images
Tabs
I set up some tabs to host all of the main content for the listing. I think this will help separate everything and make it easier for the user to navigate.
Information
There is a lot of information that goes into the listing. So I used a template for a FAQ section and separated all the information within it.




Top comments (1)
Where is the code for this?