You know those really cool portfolio sites that show a web design in a cool desktop window that’s usually from a Mac? Well, although I don’t have a Mac, I decided a year or so ago to teach myself Sass and this was one of the things I started with: a little mixin to window-fy images.

The goal is to be able to add a single class to an element and have it windowfied (I should trademark that), so we want this to be as simple as possible to drop into an existing project: we’re going to use pseudo-elements so no extra markup is required.

Mixin

The mixin looks like this (if you’re impatient just copy and paste this directly and start cracking!):

@mixin windowfy($element,$frame-color:#111,$button-size:.7vmax,$first-color:#ef5350,$second-color:#ffee58,$third-color:#66bb6a) {
  #{$element} {
    border:solid $frame-color;
    border-width:$button-size*3 $button-size/2 $button-size/2;
    border-radius:$button-size/2.5;
    position:relative;
  }
  #{$element}:before {
    background:$first-color;
    border-radius:$button-size;
    box-shadow: $button-size*1.5 0 $second-color, $button-size*3 0 $third-color;
    content:"";
    height:$button-size;
    position:absolute;
    top:$button-size*-2;
    width:$button-size;
  }
}

Oh, let’s break it down

Since I’m assuming you know how to write a mixin in Sass, let’s dive right in. The arguments the mixin variables are $element, $frame-color, $button-size, $first-color, $second-color, and $third-color. While I’d like to think they are self-explanatory, here’s an explanation anyway:

  • $element: This is the element which you want to windowfy. Can be any element. This is the only required variable.
  • $frame-color: The color of the frame itself. Default is #111.
  • $button-size: The size of the “buttons” in the window. Default is .5rem.
  • $first-color: The color of the first “button” in the window. Default is red.
  • $second-color: The color of the second “button” in the window. Default is yellow.
  • $third-color: The color of the third “button” in the window. Default is light green.

The first thing we need to do when building this is target the element. This is the only required variable and should take any element or class name. To use a Sass variable in a selector name, we need to interpolate it, which is a fancy way of saying “print this out exactly”. To do that, we wrap the variable in #{} syntax:

#{$element} {
  // styles go here
}

Now we can add base styles to the element we want to windowfy:

#{$element} {
  border:solid $frame-color;
  border-width:$button-size*3 $button-size/2 $button-size/2;
  border-radius:$button-size/2.5;
  position:relative;
}

The first style is setting the border style on all four sides, using $frame-color from the mixin. Since we’re setting a different size for the top border to represent the title bar of our faux window (faux-dow?), we set the width on the next line. The width is based on $button-size; default is 1.5rem for the top and .25rem for the sides and bottom based on a .5rem button. Next we set the border radius (also based on $button-size) to give those nice rounded corners we’re all so used to in most of our operating systems. The position style sets the parent element to relative. This ensures that the pseudo-element buttons are positioned correctly. And speaking of…

Now we add the “buttons” to our faux-window. Because we want this to be as lean as possible (KISS and DRY), we’re going to use a pseudo element to create the buttons, but leave the content value empty. The base pseudo element (in our case, :before) serves as the first button and the parent element of the other buttons, so we set the background to $first-color. Next, we set the border-radius to give us a nice circle. You could set the radius to 50% of the $button-size, but there isn’t a performance reason to do so, so I prefer to just have less math. I always prefer less math.

#{$element}:before {
  background:$first-color;
  border-radius:$button-size;
  content:"";
}

Height and width are passed through the $button-size variable, and we use aboslute positioning to keep the buttons within the faux window. Finally, to keep the buttons from being mashed up at the top of the frame we give a little space by using $button-size to calculate how far from the top we should be, using negative values to move up (positive values move down on the y axis).

#{$element}:before {
  // base styles

  height:$button-size;
  position:absolute;
  top:$button-size*-2;
  width:$button-size;
}

Now we can add the other buttons using box-shadows. Using the $button-size variable, we can set the box-shadow offsets to give spacing between the buttons so everything looks good at various sizes. Fill them with $second-color and $third-color respectively, and you have a pretty good approximation of the window controls on a Mac-type window.

#{$element}:before {
  // base styles
  // basic button styles

  box-shadow:
    $button-size*1.5 0 $second-color,
    $button-size*3 0 $third-color;
}

Usage

Now for the fun part: seeing this baby in action. Using it is as simple as calling an include and pass what element you want to use:

@include windowfy(figure);

Remember, only the element is required; default values will be used for any others you don’t pass. Below you can see the mixin on CodePen with some examples, including elements with classes and multiple nested elements. You can see the source by clicking the “scss” button.

See the Pen Windowfy Sass mixin by Smokie Lee (@xtoq) on CodePen.

Conclusion

I hope this helped someone else understand Sass a bit more, and look for more Sass articles in the future! Feel free to use this in your next project, or fork it on CodePen and do whatever you like with it. If you have any feedback, send me an email.

References