Skip to content

Instantly share code, notes, and snippets.

@jonathantneal
Last active March 18, 2024 10:47
Show Gist options
  • Save jonathantneal/d0460e5c2d5d7f9bc5e6 to your computer and use it in GitHub Desktop.
Save jonathantneal/d0460e5c2d5d7f9bc5e6 to your computer and use it in GitHub Desktop.
SASS @font-face mixin

Font Face

A mixin for writing @font-face rules in SASS.

Usage

Create a font face rule. Embedded OpenType, WOFF2, WOFF, TrueType, and SVG files are automatically sourced.

@include font-face(Samplino, fonts/Samplino);

Rendered as CSS:

@font-face {
	font-family: "Samplino";
	src: url("fonts/Samplino.eot?") format("eot"),
		 url("fonts/Samplino.woff2") format("woff2"),
		 url("fonts/Samplino.woff") format("woff"),
		 url("fonts/Samplino.ttf") format("truetype"),
		 url("fonts/Samplino.svg#Samplino") format("svg");
}

Create a font face rule that applies to bold and italic text.

@include font-face("Samplina Neue", fonts/SamplinaNeue, bold, italic);

Rendered as CSS:

@font-face {
	font-family: "Samplina Neue";
	font-style: italic;
	font-weight: bold;
	src: url("fonts/SamplinaNeue.eot?") format("eot"),
	     url("fonts/SamplinaNeue.woff2") format("woff2"),
	     url("fonts/SamplinaNeue.woff") format("woff"),
	     url("fonts/SamplinaNeue.ttf") format("truetype"),
	     url("fonts/SamplinaNeue.svg#Samplina_Neue") format("svg");
}

Create a font face rule that only sources a WOFF.

@include font-face(Samplinoff, fonts/Samplinoff, null, null, woff);

Rendered as CSS:

@font-face {
	font-family: "Samplinoff";
	src: url("fonts/Samplinoff.woff") format("woff");
}

Create a font face rule that applies to 500 weight text and sources EOT, WOFF2, and WOFF.

@include font-face(Samplinal, fonts/Samplinal, 500, normal, eot woff2 woff);

Rendered as CSS:

@font-face {
	font-family: "Samplinal";
	font-style: normal;
	font-weight: 500;
	src: url("fonts/Samplinal.eot?") format("eot"),
	     url("fonts/Samplinal.woff2") format("woff2"),
	     url("fonts/Samplinal.woff") format("woff");
}

Notes

IE≥9 prioritizes valid font formats over invalid ones. Therefore, while embedded-opentype is the correct format for an .eot font, eot is used to fool modern IE into prioritizing other, newer font formats.

IE≤8 only supports .eot fonts and parses the src property incorrectly, interpreting everything between the first opening parenthesis ( and the last closing parenthesis ) as a single URL. Therefore, a ? is appended to the .eot’s URL, fooling older IE into reading all other sources as query parameters.

// =============================================================================
// String Replace
// =============================================================================
@function str-replace($string, $search, $replace: "") {
$index: str-index($string, $search);
@if $index {
@return str-slice($string, 1, $index - 1) + $replace + str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
}
@return $string;
}
// =============================================================================
// Font Face
// =============================================================================
@mixin font-face($name, $path, $weight: null, $style: null, $exts: eot woff2 woff ttf svg) {
$src: null;
$extmods: (
eot: "?",
svg: "#" + str-replace($name, " ", "_")
);
$formats: (
otf: "opentype",
ttf: "truetype"
);
@each $ext in $exts {
$extmod: if(map-has-key($extmods, $ext), $ext + map-get($extmods, $ext), $ext);
$format: if(map-has-key($formats, $ext), map-get($formats, $ext), $ext);
$src: append($src, url(quote($path + "." + $extmod)) format(quote($format)), comma);
}
@font-face {
font-family: quote($name);
font-style: $style;
font-weight: $weight;
src: $src;
}
}
@odyright
Copy link

odyright commented Oct 8, 2019

😄 thank you

@gusbemacbe
Copy link

Hi, just as a suggestion (which I've included in the mixin I downloaded):
It would be nice to include font-display: swap so that while the fonts are loading the browser falls back to the defaults.
On mine, I have done it this way:
@font-face { font-family: quote($name); font-style: $style; font-weight: $weight; font-display: swap; src: $src; }
The font-display property is suggested by google here: https://developers.google.com/web/fundamentals/performance/optimizing-content-efficiency/webfont-optimization#the_font_display_timeline and you have probably seen it on google dev tool audits as well.

can i have your file?

Sure


@function str-replace($string, $search, $replace: "") {
	$index: str-index($string, $search);

	@if $index {
		@return str-slice($string, 1, $index - 1) + $replace +
			str-replace(str-slice($string, $index + str-length($search)), $search, $replace);
	}

	@return $string;
}


@mixin font-face($name, $path, $weight: null, $style: null, $exts: eot woff2 woff ttf svg) {
	$src: null;

	$extmods: (
		eot: "?",
		svg: "#" + str-replace($name, " ", "_")
	);

	$formats: (
		otf: "opentype",
		ttf: "truetype"
	);

	@each $ext in $exts {
		$extmod: if(map-has-key($extmods, $ext), $ext + map-get($extmods, $ext), $ext);
		$format: if(map-has-key($formats, $ext), map-get($formats, $ext), $ext);
		$src: append($src, url(quote($path + "." + $extmod)) format(quote($format)), comma);
	}

	@font-face {
		font-family: quote($name);
		font-style: $style;
		font-weight: $weight;
		font-display: swap;
		src: $src;
	}
}

it's the same, i've just added font-display: swap and nothing more than that.

@nickimola

Your SCSS does not work. I applied @mixin font-face($name: FontName, $path: fonts/FontName, $weight: null, $style: null, $exts: woff otf) and the CSS got empty.

@singpolyma
Copy link

Would be great if this had a license on it so it could be used in open source projects :)

@SvenBudak
Copy link

It makes maybe sense to add also the local rule: src: local('Roboto Light'), local('Roboto-Light'),

@pglatz
Copy link

pglatz commented Dec 2, 2020

Suppose you have a font family that has versions for normal, bold and italic (like OpenSans, OpenSans-Bold, OpenSans-Italic). Will a single rule like @include font-face("OpenSans", fonts/OpenSans, bold, italic) be smart enough to use the corresponding files. Or would you have to create a separate @font-face for each variant?

@cabb
Copy link

cabb commented Apr 30, 2021

Would be great if this had a license on it so it could be used in open source projects :)

@jonathantneal It would still be great to explicitly define the license of your code.

@richware
Copy link

My css project uses sass. So I have converted the scss to sass and have expanded to include an additional step to allow multiple font faces to be generated at one time. Here is my sass mixins: (don't forget to include the str-replace function)

@use "sass:map"

@mixin font-face-multiple($fonts)
  @each $font in $fonts
    $name: if(map-has-key($font,"name"), map-get($font,"name"), null)
    $path: if(map-has-key($font,"path"), map-get($font,"path"), null)
    $weight: if(map-has-key($font,"weight"), map-get($font,"weight"), null)
    $style: if(map-has-key($font,"style"), map-get($font,"style"), null)
    $display: if(map-has-key($font,"display"), map-get($font,"display"), null)
    $exts: if(map-has-key($font,"exts"), map-get($font,"exts"), null)
    @include font-face($name,$path,$weight,$style,$display,$exts)

@mixin font-face($name, $path, $weight: null, $style: null, $display: null, $exts: (eot woff2 woff ttf svg))
  // extensions: eot woff2 woff ttf svg
  $src: null

  $extmods: (eot:"?",svg:"#"+str-replace($name," ","_"))
  $formats: (otf: "opentype",ttf: "truetype")

  @each $ext in $exts
    $extmod: if(map-has-key($extmods, $ext), $ext + map-get($extmods, $ext), $ext)
    $format: if(map-has-key($formats, $ext), map-get($formats, $ext), $ext)
    $src: append($src, url(quote($path + "." + $extmod)) format(quote($format)), comma,)

  @font-face
    font-family: quote($name)
    font-style: $style
    font-weight: $weight
    @if($display)
      font-display: $display
    src: $src

Then all I have to do is create the mapping and include the mixin font-face-multiple and I have the code I am looking for

Example:

$font-list: ()

$font-list: append($font-list,("name": "Old Town","path": "..fonts/OldTown", "exts": (ttf)))
$font-list: append($font-list,("name": Roboto,"path": "..fonts/Roboto-Regular", "weight":400,"style":normal, "exts": (ttf)))

@include font-face-multiple($font-list)

Result:

@font-face {
  font-family: "Old Town";
  src: url("..fonts/OldTown.ttf") format("truetype");
}
@font-face {
  font-family: "Roboto";
  font-style: normal;
  font-weight: 400;
  src: url("..fonts/Roboto-Regular.ttf") format("truetype");
}

Disclaimer: The code here does not have any validation and there are some subtle issues needing resolution.

Happy coding!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment