Work Samples

Yeoman web banner generator: Adding size-scoped styles and Sass modularity.

Stack:

Core:

Node.js (22) with Yeoman Generator framework for scaffolding banner projects

Build Tools:

Gulp task runner with modular Sass compilation, Pug templating, Rollup / Babel for JS bundling, PostCSS for CSS optimization

Frontend:

Vanilla JavaScript, Pug templates, modular SCSS architecture with size-specific partials and responsive banner support

Development:

BrowserSync live reload, ESLint code linting, source maps, Puppeteer for automated screenshots

Scaffolded Output:

Generates HTML5 display ads in multiple sizes with minified assets and automated Node.js ZIP compression for ad networks

Before: Global, campaign, and local styles only

The legacy code functioned, but designers would often make changes to campaigns based on size, which required reduntant one-off style changes to each campaign banner.

Scaffolded project src before

├───campaigns
│   ├───campaign-1
│   │   ├───banners
│   │   │   ├───160x600
│   │   │   ├───300x250
│   │   │   ├───300x600
│   │   │   └───728x90
│   │   │       └───styles.scss // Local styles
│   │   ├───js
│   │   ├───pug
│   │   └───sass
│   │       └───campaign.scss // campaign 1 styles
│   └───campaign-2
│       ├───banners
│       │   ├───160x600
│       │   ├───300x250
│       │   ├───300x600
│       │   └───728x90
│       ├───js
│       ├───pug
│       └───sass
│           └───campaign.scss // campaign 2 styles
├───js
├───pug
└───sass
    └───_base.scss // universal styles 

Refactor: Added size-specific cross-campaign styles and modular sass architecture

By adding global size-scoped styles, these changes could be injected into any campain that contained a banner of that size.

Added size-scoped style modules

├───campaigns
│   ├───campaign-1 
↑   │   ├───banners
│   ↓───160x600
│   │───300x250
↑   │├───300x600 // size styles applied
│   ↓   │   └───728x90
│   │   ├───js
↑   │   ├───pug
│   ↓   └───sass
│   │       └───_campaign.scss
↑   └───campaign-2 
       ├───banners
───160x600
───300x250
├───300x600 // size styles applied
       │   └───728x90
       ├───js
       ├───pug
       └───sass
           └───_campaign.scss
───js
───pug
└───sass
    ───_base.scss
    ───_global-160x600.scss 
    ───_global-300x250.scss 
    ├───_global-300x600.scss // size-scoped style module
    ├───_global-728x90.scss 
    ├───_isi.scss  // Modular ISI styles
    └───_vars.scss // Modular variables

template\sass\styles.scss

@import '../../sass/_campaign';// Old styles
<%_ } else { _%>
@import '../../sass/_base';// Old styles
<%_ } _%>
<%_ if (size == '728x90' && isi) { _%>

// static dimensions required manual override in each banner stylesheet
#isi-wrapper{
   width: 228px;
   height: 100%;
}
<%_ } _%>
<%_ const globalSassPath = multipleCampaigns ? "../../../../sass/" : "../../sass/"; _%>

// New dimensions are dynamically assigned via Gulp
@use '<%- globalSassPath %>vars' as * with (
   $environment: $gulp-environment,
   $adWidth: $gulp-width,
   $adHeight: $gulp-height<%_ if (!isiOnAllBanners && isi) { _%>,
   $showISI: true<%_ } %>
);

@use '<%- multipleCampaigns ? `${globalSassPath}base` : "../../sass/base" %>';

<%_ if (isi) { _%>

@use '<%- globalSassPath %>isi';
<% } _%>

@use '<%- globalSassPath %>global-<%- size  %>';

<%_ if (multipleCampaigns) { _%>  

@use '../../sass/campaign';
<% } _%>

Banner height and width are assigned with SCSS variables, which are dynamically assigned for each sleected banner dimension during initialization - this means streamlined styles that can still be manually adjusted at the size, campaign, or individaul banner scope.

template\sass\sizes\_global-300x600.scss


// converted @include to @use rule in accordance with Dart Sass v3.0.0 requirements
@use 'vars' as *;

<%_ if (isiOnAnyBanner) { _%> @use 'sass:math'; <%_ } _%>
<% if (isiOnAnyBanner) { %>
    @if $showISI {
        $contentHeight: math.floor($adHeight * .66);
        $isiHeaderHeight: 2rem;
        // Sass variables are assigned recursively with Gulp, 
        #isi {
            width: $adWidth;
            height: calc($adHeight - $contentHeight);
        }
 
        #isi-header {
            height: $isiHeaderHeight;
        }

        #isi-wrapper {
            width: inherit;
            height: calc($adHeight - $contentHeight - $isiHeaderHeight);
        }
    }
<%_ } _%>

template/scss/_base.scss

#banner {
   width: $adWidth;
   height: $adHeight;
}

Dimensions are assigned to the global style module as Sass variables - Gulp overwrites these variables in each banner based on the target size. The scrolling ISI dimensions are set, and the rest of the content window takes the remainder of available space. Local styles can still override the size-scoped styles, if one-off changes are required.

gulpfile.js

.pipe($.tap((file) => {
   const folderName = path.basename(path.dirname(file.path));
   const sizes = folderName.split('x');
   file.contents = Buffer.from(
    (`$gulp-environment: ${
           development()
           ? 'development'
           : 'production'
        };\n$gulp-width: ${sizes[0]}px;\n$gulp-height: ${sizes[1]}px;\n\n`
     ).concat(String(file.contents)));
}))

~\sass\sizes\_global-300x600.scss

@use 'vars' as *;
 
<%_ if (isiOnAnyBanner) { _%> @use 'sass:math'; <%_ } _%>
 
<% if (isiOnAnyBanner) { %>
    
    @if $showISI {
    	$contentHeight: math.floor($adHeight * .66);
    	$isiHeaderHeight: 2rem;
 
    	#isi {
    		width: $adWidth;
    		height: calc($adHeight - $contentHeight);
    	}
    	#isi-header {
    		height: $isiHeaderHeight;
    	}
    	#isi-wrapper {
    		width: inherit;
    		height: calc($adHeight - $contentHeight - $isiHeaderHeight);
    	}
    }
<%_ } _%>

Modular Sass architecture

@import was deprecated in Dart Sass 1.80.0, and will removed entirely in Dart Sass 3.0.0. To future-proof the generator, I decided to convert to modules, and the @use rule with this refactor.

scaffolded/sass/campaign.scss

@import '../../../sass/_base';

@use '../../../sass/vars' as *;

scaffolded\campaigns\**\styles.scss

@use '../../../../sass/vars' as * with (
   $environment: $gulp-environment,
   $adWidth: $gulp-width,
   $adHeight: $gulp-height
);