Skip to content

Instantly share code, notes, and snippets.

@therealkevinard
Created July 27, 2017 11:15
Show Gist options
  • Save therealkevinard/8bf6cbea1b807b7d9f18a84a4a76f201 to your computer and use it in GitHub Desktop.
Save therealkevinard/8bf6cbea1b807b7d9f18a84a4a76f201 to your computer and use it in GitHub Desktop.
/node_modules/
/css/

This is the demo project for my SASS Primer article at TRKA

The project is supposed to go along with the article and it's gisted to embed in the page, but if you happen to use node\npm you can install and play with it locally.

  • There's a gulp script that watches your sass and compiles with sourcemaps
  • There's a live-reload express dev server that just plain makes life easy

To install locally:

  • clone the gist local, of course.
  • run npm install
  • To start the file watcher: npm run watch
  • To start the dev server: npm run serve
const express = require('express');
const app = express();
const livereload = require('livereload');
const lrServer = livereload.createServer();
app.use(express.static('./'));
app.listen(3000, () => {
console.log('Dev listening on http://127.0.0.1:3000/ ')
});
lrServer.watch(process.cwd());
const gulp = require('gulp');
const sass = require('gulp-sass');
const sourcemaps = require('gulp-sourcemaps');
const autoprefixer = require('gulp-autoprefixer');
gulp.task('sass', () => {
return gulp.src('scss/style.scss')
.pipe(sourcemaps.init())
.pipe(sass({
outputStyle: 'compressed',
}))
.on('error', sass.logError)
.pipe(autoprefixer({
browsers: ['last 2 versions', 'ie >= 9']
}))
.pipe(sourcemaps.write(''))
.pipe(gulp.dest('css'));
});
gulp.task('watch-sass', ['sass'], () => {
gulp.watch([
'scss/**/*.scss'
], ['sass']);
})
gulp.task('default', ['watch-sass']);
<html>
<head>
<link rel="stylesheet" href="css/style.css">
</head>
<body>
<header id="sxn-head">
<h1>SCSS Playground</h1>
</header>
<section id="sxn-teaser">
<h2>This is h2</h2>
<h3>This is h3</h3>
<h4>This is h4</h4>
<h5>This is h5</h5>
<h6>This is h6</h6>
</section>
<section id="sxn-content">
<div>
<h4>Filled Buttons</h4>
<span class="button primary">Primary</span>
<span class="button secondary">Secondary</span>
<span class="button success">Success</span>
<span class="button warning">Warning</span>
<span class="button alert">Alert</span>
</div>
<div>
<h4>Hollow Buttons</h4>
<span class="button hollow primary ">Primary</span>
<span class="button hollow secondary ">Secondary</span>
<span class="button hollow success ">Success</span>
<span class="button hollow warning ">Warning</span>
<span class="button hollow alert ">Alert</span>
</div>
<div>
<h4>Shaded Buttons</h4>
<span class="button shaded primary ">Primary</span>
<span class="button shaded secondary ">Secondary</span>
<span class="button shaded success ">Success</span>
<span class="button shaded warning ">Warning</span>
<span class="button shaded alert ">Alert</span>
</div>
</section>
<footer id="site-footer">
<p>A paragraph in a footer</p>
</footer>
<script>document.write('<script src="http://' + (location.host || 'localhost').split(':')[0] + ':35729/livereload.js"></' + 'script>')</script>
</body>
</html>
{
"name": "sass-primer-005",
"version": "1.0.0",
"description": "",
"main": "index.js",
"scripts": {
"watch": "gulp",
"serve": "node app.js"
},
"keywords": [],
"author": {
"name": "Kevin Ard",
"email": "[email protected]",
"url": "https://therealkevinard.com/"
},
"license": "MIT",
"dependencies": {
"express": "^4.15.3",
"gulp": "^3.9.1",
"gulp-autoprefixer": "^4.0.0",
"gulp-sass": "^3.1.0",
"gulp-sourcemaps": "^2.6.0",
"livereload": "^0.6.2"
}
}
/*
Our buttons' core scss.
--
Note that the scss in this file can read any variables that have been set up to the point
when the compiler loads it.
*/
//-- our core button code
@mixin button-base {
display: inline-block;
vertical-align: middle;
margin: $button-margin;
font-family: $button-font-family;
padding: $button-padding;
-webkit-appearance: none;
border: 1px solid transparent;
border-radius: $button-radius;
transition: $button-transition;
font-size: $button-font-size-default;
line-height: 1;
text-align: center;
cursor: pointer;
}
//-- paints look-and-feel over our core button code.
@mixin button-style(
$background: $button-background,
$background-hover: $button-background-hover,
$color: $button-color,
$background-hover-lightness: $button-background-hover-lightness
) {
@include button-base;
background-color: $background;
color: $color;
&:hover, &:focus {
background-color: $background-hover;
color: $color;
}
}
//-- to make a hollow button, remove background fills
@mixin button-hollow {
&,
&:hover, &:focus {
background-color: transparent;
}
&.disabled,
&[disabled] {
&,
&:hover, &:focus {
background-color: transparent;
}
}
}
//-- paints border and hover styles over button-hollow
@mixin button-hollow-style(
$color: $button-background,
$hover-lightness: $button-hollow-hover-lightness,
$border-width: $button-hollow-border-width
) {
@include button-base;
$color-hover: scale-color($color, $lightness: $hover-lightness);
border: $border-width solid $color;
color: $color;
&:hover, &:focus {
border-color: $color-hover;
color: $color-hover;
&.disabled,
&[disabled] {
border: $border-width solid $color;
color: $color;
}
}
}
/*
The "main" mixin that pulls it all together.
--
This one @includes the main button-base mixin, and what parameters you give it when you call
it decide the look and feel of your final button. You can do a *whole lot* with very little code this way - it's all offloaded
to a common mixin that you can reuse a million different ways.
*/
@mixin button(
$style: hollow,
$background: $button-background,
$background-hover: $button-background-hover,
$color: $button-color
) {
@if $style == solid {
@include button-style($background, $background-hover, $color);
} @else if $style == shaded {
@include button-style(rgba($background, 0.2), rgba($background, 0.6), $color);
} @else {
// this is our default, if the style provided doesn't match.
// this will make a default hollow button style
@include button-hollow;
@include button-hollow-style($background);
}
}
/*
button-specific variables
--
When using scoped variables like we are here, it's good practice to use the !default keyword.
!default sort of translates to "set this variable, UNLESS it's already defined.
This way, we can set the variables in this file but if, eg, we set $button-padding in our theme variables file this won't override it.
*/
$button-font-family: inherit !default;
$button-font-size-default: 0.943rem;
$button-transition: background-color 0.25s ease-out, color 0.25s ease-out !default;
$button-padding: 0.85em 1em !default;
$button-margin: 0 0 $global-margin 0 !default;
$button-color: $white !default;
$button-radius: $global-radius !default;
$button-background: $primary-color !default;
$button-background-hover-lightness: -20% !default;
$button-background-hover: scale-color($button-background, $lightness: -15%) !default;
$button-hollow-border-width: 1px !default;
$button-hollow-hover-lightness: -50% !default;
$button-color-palette: $named-palette !default;
// import the scss
@import "base";
.button {
//-- pull in our core styles.
@include button;
/*
remember I said we were going to do some magic with the list in theme/_variables.scss?
when we have a list in sass, we can 'walk' it and repeat the same things - but a little differently - with each item.
this is a familiar thing for the dev crowd, but it may be new to designers.
@each $name, $color in $button-color-palette{} translates to:
"get $button-color-palette, then for each thing in it: set variables $name from its left side and $color from its right side. Now do this with those variables..."
I'm also sneaking in the /#/{/$variable/} syntax. I'm not saying the real word for it just yet because it's scary, but what it does:
Usually variables are treated as values, but /#/{/$variable} makes them be treated basically as if you'd typed it in as a word. It's good for a lot of things, but in this case it's letting us use a variable as a class name.
The word: "String Interpolation". See why I didn't lead with that? :)
*/
@each $name, $color in $button-color-palette {
//-- make a filled button with that color and class name
&.#{$name} {
@include button(solid, $color, lighten($color, 20%), $white);
}
//-- make a hollow button with the color and class name
&.hollow.#{$name} {
@include button(hollow, $color);
}
&.shaded.#{$name} {
@include button(shaded, $color, $color, $color);
}
}
}
section, header, footer {
}
//-- set default vertcal pads from the list in our theme file
@each $type, $padding in $section-paddings {
#{$type} {
padding-top: $padding;
padding-bottom: $padding;
}
}
//-- paint the sections
header {
background-color: $header-background;
padding-top: $global-padding;
padding-right:$global-padding*2;
padding-left:$global-padding*2;
border-bottom: map_get($border-sizes, light) solid $black;
}
footer {
background-color: $header-background;
padding-bottom: $global-padding;
padding-right:$global-padding*2;
padding-left:$global-padding*2;
border-top: map_get($border-sizes, light) solid $black;
}
section {
padding-right:$global-padding*4;
padding-left:$global-padding*4;
&:first-of-type {
padding-top: $global-padding;
}
&:last-of-type {
padding-bottom: $global-padding;
}
}
section {
&#sxn-teaser {
background-color: $medium-gray;
}
&#sxn-content {
background-color: $light-gray;
}
}
$section-padding: $global-padding !default;
$header-background: $dark-gray !default;
@import "base";
@import "named-sxns";
/**
I use sass index files a lot - the @imports that only @import other related files.
Some people use the naming convention of calling them _index.scss, but I use the conv
of naming the index file with its parent folder name.
So theme/_theme.scss is the index file for theme.
The reason is personal: my ide has project search, and it's a lot easier to find _theme.scss than _index.scss when there are a dozen other _index.scss'
*/
//-- our main theme setup: its theme-wide variables, mixins, etc
@import "theme/theme";
//-- 'scoped' scss files
@import "sections/sections";
@import "typography/typography";
@import "buttons/buttons";
/*
Converts px size to rems.
*/
@function calculateRem($size) {
$remSize: $size / 16;
@return #{$remSize}rem;
}
/*
The three global theme files:
- functions: These are utilities that *create* values and they don't require variables; they load first
- variables: These are set theme values. They're used by mixins, so they load before mixins;
- mixins: These are the take-it-or-leave-it @include blocks. They load after the other two because they need the other two.
*/
@import "functions";
@import "variables";
@import "mixins";
//-- global style resets
html, body {
margin: 0;
// set our font size here to the same value used in the calculateRem function so rems will... work.
font-size: 16px;
}
//-- geometry
$global-margin: 1rem;
$global-padding: 1rem;
$global-radius: 0;
$global-width: calculateRem(1200);
//---- sections
$section-padding: calculateRem(80);
$section-paddings: (
'header': calculateRem(20),
'footer': calculateRem(20),
'section': calculateRem(40)
);
$border-sizes: (
'light': 1px,
'heavy': 2px,
);
//-- typo
$global-lineheight: 1.5;
$font-base: 'Helvetica Neue', Helvetica, Roboto, Arial, sans-serif;
$global-weight-normal: normal;
$global-weight-bold: bold;
//---- headers
// nested list
$header-font-family: $font-base;
$header-styles: (
'h1': ('font-size': 32),
'h2': ('font-size': 28),
'h3': ('font-size': 22),
'h4': ('font-size': 18),
'h5': ('font-size': 16),
'h6': ('font-size': 16),
);
//---- body
$body-font-family: $font-base;
//-- colors
//---- spot colors
$primary-color: #ff9b83;
$light-gray: #e6e6e6;
$medium-gray: #cacaca;
$dark-gray: #8a8a8a;
$black: #0a0a0a;
$white: #fefefe;
//---- named list
// this is a list. it lets you name a stack of related things. We're going to do some magic with lists.
$named-palette: (
primary: #1779ba,
secondary: #767676,
success: #3adb76,
warning: #ffae00,
alert: #cc4b36,
);
dl, dt, dd, ul, ol, li, h1, h2, h3, h4, h5, h6, p, blockquote, th, td {
margin: 0;
padding: 0;
}
p {
margin-bottom: $paragraph-margin-bottom;
font-size: inherit;
line-height: $paragraph-lineheight;
text-rendering: $paragraph-text-rendering;
}
em,
i {
font-style: italic;
line-height: inherit;
}
strong,
b {
font-weight: $global-weight-bold;
line-height: inherit;
}
small {
font-size: $small-font-size;
line-height: inherit;
}
h1,
h2,
h3,
h4,
h5,
h6 {
font-family: $header-font-family;
font-style: $header-font-style;
font-weight: $header-font-weight;
color: $header-color;
text-rendering: $header-text-rendering;
margin-bottom: $header-margin-bottom;
small {
line-height: 0;
color: $header-small-font-color;
}
}
@each $header, $styles in $header-styles {
#{$header} {
$fontsize_temp: map-get($styles, font-size);
font-size: calculateRem($fontsize_temp);
}
}
$header-font-family: $body-font-family !default;
$header-font-weight: $global-weight-normal !default;
$header-font-style: normal !default;
$header-color: inherit !default;
$header-lineheight: 1.4 !default;
$header-margin-bottom: 0.5rem !default;
$header-text-rendering: optimizeLegibility !default;
$small-font-size: 80% !default;
$header-small-font-color: $medium-gray !default;
$paragraph-lineheight: 1.6 !default;
$paragraph-margin-bottom: 1rem !default;
$paragraph-text-rendering: optimizeLegibility !default;
@import "base";
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment