├── .gitignore
├── .jshintrc
├── Gruntfile.js
├── LICENSE
├── README-image.md
├── README.md
├── afontgarde.css
├── afontgarde.js
├── docs.css
├── fonts
├── icomoon.eot
├── icomoon.svg
├── icomoon.ttf
├── icomoon.woff
└── png
│ └── hamburger.png
├── lib
├── faceoff.js
└── modernizr.fontface-generatedcontent.js
├── markup-image.html
├── markup-tests.html
├── markup.html
├── package.json
└── src
├── afontgarde.tmpl.css
├── afontgarde.tmpl.js
└── banner
/.gitignore:
--------------------------------------------------------------------------------
1 | node_modules/
2 | bower_components/
--------------------------------------------------------------------------------
/.jshintrc:
--------------------------------------------------------------------------------
1 | {
2 | "curly": true,
3 | "eqeqeq": true,
4 | "immed": true,
5 | "latedef": true,
6 | "newcap": true,
7 | "noarg": true,
8 | "sub": true,
9 | "undef": true,
10 | "unused": true,
11 | "boss": true,
12 | "eqnull": true,
13 | "node": true
14 | }
--------------------------------------------------------------------------------
/Gruntfile.js:
--------------------------------------------------------------------------------
1 |
2 | /*global module:false require*/
3 | module.exports = function(grunt) {
4 | "use strict";
5 |
6 | // Project configuration.
7 | grunt.initConfig({
8 | // Metadata.
9 | pkg: grunt.file.readJSON('package.json'),
10 | banner: grunt.file.read( 'src/banner' ),
11 | jshint: {
12 | gruntfile: {
13 | options: {
14 | jshintrc: '.jshintrc'
15 | },
16 | src: 'Gruntfile.js'
17 | },
18 | src: {
19 | src: [ '<%= pkg.name %>.js' ]
20 | }
21 | },
22 | watch: {
23 | gruntfile: {
24 | files: '<%= jshint.gruntfile.src %>',
25 | tasks: ['jshint:gruntfile']
26 | },
27 | src: {
28 | files: [ '<%= concat.js.src %>', '<%= concat.css.src %>' ],
29 | tasks: [ 'concat', 'replace' ]
30 | }
31 | },
32 | bytesize: {
33 | src: {
34 | src: [
35 | '<%= pkg.name %>.css',
36 | '<%= pkg.name %>.js'
37 | ]
38 | }
39 | },
40 | concat: {
41 | options: {
42 | stripBanners: false,
43 | banner: '<%= banner %>'
44 | },
45 | js: {
46 | src: [ 'node_modules/fontfaceonload/dist/fontfaceonload.js', 'src/<%= pkg.name %>.tmpl.js' ],
47 | dest: '<%= pkg.name %>.js'
48 | },
49 | css: {
50 | src: [ 'src/<%= pkg.name %>.tmpl.css' ],
51 | dest: '<%= pkg.name %>.css'
52 | }
53 | },
54 | 'gh-pages': {
55 | options: {},
56 | src: ['.gitignore', '*.js', '*.css', '*.html', 'lib/*', 'fonts/**/*' ]
57 | },
58 | replace: {
59 | dist: {
60 | options: {
61 | patterns: [
62 | {
63 | match: /\{\{(\w*)\}\}/g,
64 | replacement: function( match, key ) {
65 | return grunt.template.process( "<%= pkg.config." + key + " %>" );
66 | }
67 | }
68 | ]
69 | },
70 | files: [
71 | {
72 | expand: true,
73 | flatten: true,
74 | src: [ '<%= pkg.name %>.css', '<%= pkg.name %>.js' ],
75 | dest: './'
76 | }
77 | ]
78 | }
79 | }
80 | });
81 |
82 | require('matchdep').filterDev('grunt-*').forEach(grunt.loadNpmTasks);
83 |
84 | // Default task.
85 | grunt.registerTask('default', ['concat', 'replace', 'jshint', 'bytesize:src']);
86 |
87 | };
88 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2014 Filament Group
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of
6 | this software and associated documentation files (the "Software"), to deal in
7 | the Software without restriction, including without limitation the rights to
8 | use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
9 | the Software, and to permit persons to whom the Software is furnished to do so,
10 | subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
17 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
18 | COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19 | IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20 | CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
21 |
--------------------------------------------------------------------------------
/README-image.md:
--------------------------------------------------------------------------------
1 | # Critical Icons
2 |
3 | ## Fallback to Image
4 |
5 | Warning: Try to use the other three a-font-garde use cases on the main README before moving forward with this use case. It is less reliable.
6 |
7 | ### [Demo](http://filamentgroup.github.io/a-font-garde/markup-image.html)
8 |
9 |
10 | Use a bitmap image like a PNG for better fallback compatibility.
11 |
12 | Requires a `@font-face` feature test like Modernizr that provides the `supports-fontface` class to operate correctly.
13 |
14 | * Modernizr
15 | * [Mat’s `face-off`](https://github.com/filamentgroup/face-off)
16 | * [Pixel Ambacht](http://pixelambacht.nl/2013/font-face-render-check/): Careful if you support IE8. This test requires an external request and thus may have a race condition for if you’re using background-image for a fallback.
17 |
18 | ### HTML for fallback to Bitmap
19 |
20 |
21 |
22 |
23 | Menu
24 |
25 |
26 | ### CSS for fallback to Bitmap
27 |
28 | .icon-fallback-img .icon-hamburger {
29 | /* Adjust to match the icon font size */
30 | width: 1em;
31 | height: 1em;
32 | /* Note: BB5 doesn’t support background-images on pseudo-elements */
33 | background: url("fonts/png/hamburger.png") no-repeat;
34 | }
35 | /* A-Grade */
36 | .supports-fontface .icon-fallback-img .icon-hamburger:before {
37 | font-family: icomoon;
38 | content: "\e601";
39 | }
40 |
41 | The fallback background-image is less reliable, since it does not check to make sure the icon font has successfully loaded. We do this so that the background-image request will not be prematurely triggered. If the HTTP request for the font fails, this will show the default Unicode character for `"\e601"`.
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | :warning: This project is archived and the repository is no longer maintained.
2 |
3 | # A Font Garde
4 |
5 | [ ](http://www.filamentgroup.com/)
6 |
7 | **A set of reliable (nay, bulletproof) patterns for icon fonts.**
8 |
9 | To start, you’ll probably want to **[read the Filament Group Blog Post](http://filamentgroup.com/lab/bulletproof_icon_fonts)**.
10 |
11 | Then, add `afontgarde.css` and `afontgarde.js` to your build to concat up into your web site.
12 |
13 | ## Uses Cases:
14 |
15 | 1. Critical Icon
16 | * Includes fallback text of varying length
17 | * Has size restrictions, fallback must have a similar size
18 | * Fallback to a [reliable Unicode equivalent](http://unicode.johnholtripley.co.uk/) (note: very few reliable cross-platform glyphs exist)
19 | * [Fallback to an image](README-image.md) (not recommended)
20 | 1. Icon as decoration, does not need a fallback but must not take up space (for proper centering/alignment of neighboring content)
21 |
22 | ### Support List
23 |
24 | 1. CSS including `:before`, `:after` pseudo-elements and `@font-face`
25 | 1. JS (`@font-face` loading test)
26 |
27 | ### [Demos](http://filamentgroup.github.io/a-font-garde/markup.html)
28 |
29 | ## Decorative Icons (No Fallback Required)
30 |
31 | ### HTML
32 |
33 |
34 | Share on Twitter (Sibling Text)
35 |
36 | Make sure you use sibling text here for labeling text—don’t nest the text inside of the icon `span`. We need `aria-hidden` on the icon to make sure it is not read aloud by screen readers.
37 |
38 | ### CSS
39 |
40 | .fontloaded .icon:before {
41 | font-family: icomoon;
42 | }
43 | .fontloaded .icon-twitter:before {
44 | content: "\e604";
45 | }
46 |
47 | The `fontloaded` class is added by the FontFaceOnload script, which checks to make sure the Icomoon font has successfully loaded (just because a browser supports font-face doesn’t mean the request will succeed).
48 |
49 | ## Critical Icons with Fallback Text
50 |
51 | If the icon fails, the text must show. Otherwise hide it.
52 |
53 | ### HTML
54 |
55 |
56 |
57 | Fallback Text
58 |
59 |
60 | ### CSS
61 |
62 | *Reuse the decorative icon CSS above.*
63 |
64 | ## Critical Icons with Fallback Icons
65 |
66 | If the icon fails, a fallback icon is shown. Otherwise hide the default icon.
67 |
68 | ### HTML for fallback to Unicode Glyph
69 |
70 |
71 |
72 | Menu
73 |
74 |
75 | ### CSS for fallback to Unicode Glyph
76 |
77 | .icon-fallback-glyph .icon-hamburger:before {
78 | content: "\2261"; /* Hamburger */
79 | /* Adjust to match the icon font size */
80 | font-size: 2em;
81 | line-height: .5;
82 | }
83 | /* A-Grade */
84 | .fontloaded .icon-fallback-glyph .icon-hamburger:before {
85 | content: "\e601";
86 | }
87 |
88 | Choose your fallback glyph character with care. Cross-browser/platform compatibility may vary. Check John Holt Ripley’s [compatibility tables]( http://unicode.johnholtripley.co.uk/).
89 |
90 | ## JavaScript
91 |
92 | The JavaScript adds the appropriate classes to make sure that the font has loaded.
93 |
94 | ```
95 | AFontGarde( 'icomoon', '\uE600\uE601\uE602\uE605' );
96 | ```
97 |
98 | ### Full options list
99 | ```
100 | AFontGarde( 'icomoon', {
101 | glyphs: '\uE600\uE601\uE602\uE605',
102 | success: function() {},
103 | error: function() {},
104 | timeout: 10000
105 | });
106 | ```
107 |
108 | The first argument is the name of the `font-family`. The second argument is a few of the glyphs contained in the new font. We measure these characters to make sure the font has loaded successfully.
109 |
110 | ## Browser Support
111 |
112 | These browsers were tested, full browser support is more comprehensive:
113 |
114 | * Chrome 34
115 | * Firefox 29
116 | * Safari 7
117 | * iOS 6, iOS 7 Mobile Safari
118 | * Opera 12
119 | * Blackberry OS 7
120 | * Android 2.3
121 | * Internet Explorer 8, 9, 10, 11
122 |
123 | ### Fallback Experience
124 |
125 | * Opera Mini
126 | * Windows Phone 7.5 (Note: The icon-fallback-img method fails here because of a Modernizr false positive—an issue has been filed and resolved)
127 | * Opera 9
128 | * Blackberry OS 5
129 | * Blackberry OS 6 (technically supports SVG @font-face, but it’s horribly buggy. So we isolate the SVG entry to newer WebKit and opt into the fallback experience)
130 | * Internet Explorer 7 (The icon-fallback-glyph method falls back to text instead of an image due to a lack of :before/:after support. Requires additional Modernizr classes, noted below)
131 |
132 | ## Options
133 |
134 | ### Internet Explorer 7 and below
135 |
136 | To add support for Internet Explorer 7 and other browsers that don’t support `pseudo-elements` (`:before`, `:after`) use the Modernizr library to provide the pseudo-elements feature test for the `supports-generatedcontent` and `supports-no-generatedcontent` classes.
137 |
138 | You can configure Modernizr with the `supports-` classes prefix (make sure to include the `generatedcontent` test) or you can change the `supports-` prefix in a-font-garde. See the “Changing the CSS Prefix” section below for more information.
139 |
140 | ### Changing the `supports-` CSS Prefix
141 |
142 | To use a different CSS Prefix without editing the raw JS and CSS manually, you can optionally clone the repository and change the configuration setting in `package.json`.
143 |
144 | ```
145 | "config": {
146 | "cssprefix": "supports-"
147 | }
148 | ```
149 |
150 | Modify with your own CSS prefix and run `grunt` to generate new `afontgarde.css` and `afontgarde.js` files.
151 |
--------------------------------------------------------------------------------
/afontgarde.css:
--------------------------------------------------------------------------------
1 | /*! afontgarde - v0.1.6 - 2015-03-13
2 | * https://github.com/filamentgroup/a-font-garde
3 | * Copyright (c) 2015 Filament Group c/o Zach Leatherman
4 | * MIT License */
5 |
6 | .icon-fallback-text .icon {
7 | display: none;
8 | }
9 | /*
10 | ADDED BY afontgarde.js:
11 | Note: sure .FONT_NAME comes first for adjoining classes bug in IE7.
12 |
13 | .FONT_NAME.supports-generatedcontent .icon-fallback-text .icon {
14 | display: inline-block;
15 | }*/
16 |
17 | .icon-fallback-img .text,
18 | .icon-fallback-glyph .text/*,
19 | ADDED BY afontgarde.js:
20 | Note: sure .FONT_NAME comes first for adjoining classes bug in IE7.
21 |
22 | .FONT_NAME.supports-generatedcontent .icon-fallback-text .text*/ {
23 | /* visually hide but accessible (h5bp.com) */
24 | clip: rect(0 0 0 0);
25 | overflow: hidden;
26 | position: absolute;
27 | height: 1px;
28 | width: 1px;
29 | }
30 |
31 | /* Careful, don’t use adjoining classes here (IE7) */
32 | .supports-no-generatedcontent .icon-fallback-glyph .text {
33 | clip: auto;
34 | overflow: visible;
35 | position: static;
36 | height: auto;
37 | width: auto;
38 | }
39 | /*
40 | ADDED BY afontgarde.js:
41 | .FONT_NAME .icon-fallback-glyph .icon:before {
42 | // inherit for font-size, line-height was not working on IE8
43 | font-size: 1em;
44 | font-size: inherit;
45 | line-height: 1;
46 | line-height: inherit;
47 | }*/
48 | .icon-fallback-img .icon {
49 | display: inline-block;
50 | }
51 | .icon-fallback-img .icon:before {
52 | content: "";
53 | }
54 | /* The img fallback version is not as reliable since it does not check to make sure the fontloaded font has loaded. If we did add the .fontloaded class, it would unnecessarily request the fallback image. */
55 | .supports-fontface.supports-generatedcontent .icon-fallback-img .icon {
56 | background-image: none;
57 | }
--------------------------------------------------------------------------------
/afontgarde.js:
--------------------------------------------------------------------------------
1 | /*! afontgarde - v0.1.6 - 2015-03-13
2 | * https://github.com/filamentgroup/a-font-garde
3 | * Copyright (c) 2015 Filament Group c/o Zach Leatherman
4 | * MIT License */
5 |
6 | /*! fontfaceonload - v0.1.6 - 2015-03-13
7 | * https://github.com/zachleat/fontfaceonload
8 | * Copyright (c) 2015 Zach Leatherman (@zachleat)
9 | * MIT License */
10 |
11 | ;(function( win, doc ) {
12 | "use strict";
13 |
14 | var TEST_STRING = 'AxmTYklsjo190QW',
15 | SANS_SERIF_FONTS = 'sans-serif',
16 | SERIF_FONTS = 'serif',
17 |
18 | // lighter and bolder not supported
19 | weightLookup = {
20 | normal: '400',
21 | bold: '700'
22 | },
23 |
24 | defaultOptions = {
25 | tolerance: 2, // px
26 | delay: 100,
27 | glyphs: '',
28 | success: function() {},
29 | error: function() {},
30 | timeout: 5000,
31 | weight: '400', // normal
32 | style: 'normal'
33 | },
34 |
35 | // See https://github.com/typekit/webfontloader/blob/master/src/core/fontruler.js#L41
36 | style = [
37 | 'display:block',
38 | 'position:absolute',
39 | 'top:-999px',
40 | 'left:-999px',
41 | 'font-size:48px',
42 | 'width:auto',
43 | 'height:auto',
44 | 'line-height:normal',
45 | 'margin:0',
46 | 'padding:0',
47 | 'font-variant:normal',
48 | 'white-space:nowrap'
49 | ],
50 | html = '
' + TEST_STRING + '
';
51 |
52 | var FontFaceOnloadInstance = function() {
53 | this.fontFamily = '';
54 | this.appended = false;
55 | this.serif = undefined;
56 | this.sansSerif = undefined;
57 | this.parent = undefined;
58 | this.options = {};
59 | };
60 |
61 | FontFaceOnloadInstance.prototype.getMeasurements = function () {
62 | return {
63 | sansSerif: {
64 | width: this.sansSerif.offsetWidth,
65 | height: this.sansSerif.offsetHeight
66 | },
67 | serif: {
68 | width: this.serif.offsetWidth,
69 | height: this.serif.offsetHeight
70 | }
71 | };
72 | };
73 |
74 | FontFaceOnloadInstance.prototype.load = function () {
75 | var startTime = new Date(),
76 | that = this,
77 | serif = that.serif,
78 | sansSerif = that.sansSerif,
79 | parent = that.parent,
80 | appended = that.appended,
81 | dimensions,
82 | options = this.options,
83 | ref = options.reference;
84 |
85 | function getStyle( family ) {
86 | return style
87 | .concat( [ 'font-weight:' + options.weight, 'font-style:' + options.style ] )
88 | .concat( "font-family:" + family )
89 | .join( ";" );
90 | }
91 |
92 | var sansSerifHtml = html.replace( /\%s/, getStyle( SANS_SERIF_FONTS ) ),
93 | serifHtml = html.replace( /\%s/, getStyle( SERIF_FONTS ) );
94 |
95 | if( !parent ) {
96 | parent = that.parent = doc.createElement( "div" );
97 | }
98 |
99 | parent.innerHTML = sansSerifHtml + serifHtml;
100 | sansSerif = that.sansSerif = parent.firstChild;
101 | serif = that.serif = sansSerif.nextSibling;
102 |
103 | if( options.glyphs ) {
104 | sansSerif.innerHTML += options.glyphs;
105 | serif.innerHTML += options.glyphs;
106 | }
107 |
108 | function hasNewDimensions( dims, el, tolerance ) {
109 | return Math.abs( dims.width - el.offsetWidth ) > tolerance ||
110 | Math.abs( dims.height - el.offsetHeight ) > tolerance;
111 | }
112 |
113 | function isTimeout() {
114 | return ( new Date() ).getTime() - startTime.getTime() > options.timeout;
115 | }
116 |
117 | (function checkDimensions() {
118 | if( !ref ) {
119 | ref = doc.body;
120 | }
121 | if( !appended && ref ) {
122 | ref.appendChild( parent );
123 | appended = that.appended = true;
124 |
125 | dimensions = that.getMeasurements();
126 |
127 | // Make sure we set the new font-family after we take our initial dimensions:
128 | // handles the case where FontFaceOnload is called after the font has already
129 | // loaded.
130 | sansSerif.style.fontFamily = that.fontFamily + ', ' + SANS_SERIF_FONTS;
131 | serif.style.fontFamily = that.fontFamily + ', ' + SERIF_FONTS;
132 | }
133 |
134 | if( appended && dimensions &&
135 | ( hasNewDimensions( dimensions.sansSerif, sansSerif, options.tolerance ) ||
136 | hasNewDimensions( dimensions.serif, serif, options.tolerance ) ) ) {
137 |
138 | options.success();
139 | } else if( isTimeout() ) {
140 | options.error();
141 | } else {
142 | if( !appended && "requestAnimationFrame" in window ) {
143 | win.requestAnimationFrame( checkDimensions );
144 | } else {
145 | win.setTimeout( checkDimensions, options.delay );
146 | }
147 | }
148 | })();
149 | }; // end load()
150 |
151 | FontFaceOnloadInstance.prototype.checkFontFaces = function( timeout ) {
152 | var _t = this;
153 | doc.fonts.forEach(function( font ) {
154 | if( font.family.toLowerCase() === _t.fontFamily.toLowerCase() &&
155 | ( weightLookup[ font.weight ] || font.weight ) === ''+_t.options.weight &&
156 | font.style === _t.options.style ) {
157 | font.load().then(function() {
158 | _t.options.success();
159 | win.clearTimeout( timeout );
160 | });
161 | }
162 | });
163 | };
164 |
165 | FontFaceOnloadInstance.prototype.init = function( fontFamily, options ) {
166 | var timeout;
167 |
168 | for( var j in defaultOptions ) {
169 | if( !options.hasOwnProperty( j ) ) {
170 | options[ j ] = defaultOptions[ j ];
171 | }
172 | }
173 |
174 | this.options = options;
175 | this.fontFamily = fontFamily;
176 |
177 | // For some reason this was failing on afontgarde + icon fonts.
178 | if( !options.glyphs && "fonts" in doc ) {
179 | if( options.timeout ) {
180 | timeout = win.setTimeout(function() {
181 | options.error();
182 | }, options.timeout );
183 | }
184 |
185 | this.checkFontFaces( timeout );
186 | } else {
187 | this.load();
188 | }
189 | };
190 |
191 | var FontFaceOnload = function( fontFamily, options ) {
192 | var instance = new FontFaceOnloadInstance();
193 | instance.init(fontFamily, options);
194 |
195 | return instance;
196 | };
197 |
198 | // intentional global
199 | win.FontFaceOnload = FontFaceOnload;
200 | })( this, this.document );
201 |
202 | /*
203 | * A Font Garde
204 | */
205 |
206 | ;(function( w ) {
207 |
208 | var doc = w.document,
209 | ref,
210 | css = ['.FONT_NAME.supports-generatedcontent .icon-fallback-text .icon { display: inline-block; }',
211 | '.FONT_NAME.supports-generatedcontent .icon-fallback-text .text { clip: rect(0 0 0 0); overflow: hidden; position: absolute; height: 1px; width: 1px; }',
212 | '.FONT_NAME .icon-fallback-glyph .icon:before { font-size: 1em; font-size: inherit; line-height: 1; line-height: inherit; }'];
213 |
214 | function addEvent( type, callback ) {
215 | if( 'addEventListener' in w ) {
216 | return w.addEventListener( type, callback, false );
217 | } else if( 'attachEvent' in w ) {
218 | return w.attachEvent( 'on' + type, callback );
219 | }
220 | }
221 |
222 | // options can be a string of glyphs or an options object to pass into FontFaceOnload
223 | AFontGarde = function( fontFamily, options ) {
224 | var fontFamilyClassName = fontFamily.toLowerCase().replace( /\s/g, '' ),
225 | executed = false;
226 |
227 | function init() {
228 | if( executed ) {
229 | return;
230 | }
231 | executed = true;
232 |
233 | if( typeof FontFaceOnload === 'undefined' ) {
234 | throw 'FontFaceOnload is a prerequisite.';
235 | }
236 |
237 | if( !ref ) {
238 | ref = doc.getElementsByTagName( 'script' )[ 0 ];
239 | }
240 | var style = doc.createElement( 'style' ),
241 | cssContent = css.join( '\n' ).replace( /FONT_NAME/gi, fontFamilyClassName );
242 |
243 | style.setAttribute( 'type', 'text/css' );
244 | if( style.styleSheet ) {
245 | style.styleSheet.cssText = cssContent;
246 | } else {
247 | style.appendChild( doc.createTextNode( cssContent ) );
248 | }
249 | ref.parentNode.insertBefore( style, ref );
250 |
251 | var opts = {
252 | timeout: 5000,
253 | success: function() {
254 | // If you’re using more than one icon font, change this classname (and in a-font-garde.css)
255 | doc.documentElement.className += ' ' + fontFamilyClassName;
256 |
257 | if( options && options.success ) {
258 | options.success();
259 | }
260 | }
261 | };
262 |
263 | // These characters are a few of the glyphs from the font above */
264 | if( typeof options === "string" ) {
265 | opts.glyphs = options;
266 | } else {
267 | for( var j in options ) {
268 | if( options.hasOwnProperty( j ) && j !== "success" ) {
269 | opts[ j ] = options[ j ];
270 | }
271 | }
272 | }
273 |
274 | FontFaceOnload( fontFamily, opts );
275 | }
276 |
277 | // MIT credit: filamentgroup/shoestring
278 | addEvent( "DOMContentLoaded", init );
279 | addEvent( "readystatechange", init );
280 | addEvent( "load", init );
281 |
282 | if( doc.readyState === "complete" ){
283 | init();
284 | }
285 | };
286 |
287 | })( this );
--------------------------------------------------------------------------------
/docs.css:
--------------------------------------------------------------------------------
1 | /* Logo */
2 | .header {
3 | background: #247201 url(http://filamentgroup.com/images/headerbg-new.jpg) no-repeat bottom left;
4 | }
5 | #fg-logo {
6 | text-indent: -9999px;
7 | margin: 0 auto;
8 | width: 287px;
9 | height: 52px;
10 | background-image: url(http://filamentgroup.com/images/fg-logo-icon.png);
11 | }
12 | @media (-webkit-min-device-pixel-ratio: 1.5), (min-device-pixel-ratio: 1.5){
13 | #fg-logo {
14 | background-size: 287px 52px;
15 | background-image: url(http://filamentgroup.com/images/fg-logo-icon-lrg.png);
16 | }
17 | }
18 | /* Demo styles */
19 | body {
20 | font-family: sans-serif;
21 | font-size: 100%;
22 | }
23 | .docs-main {
24 | margin: 1em auto;
25 | max-width: 46em;
26 | }
27 | label {
28 | display: block;
29 | margin: 1em 0;
30 | }
31 | input,
32 | textarea {
33 | display: block;
34 | width: 100%;
35 | -webkit-box-sizing: border-box;
36 | -moz-box-sizing: border-box;
37 | box-sizing: border-box;
38 |
39 | margin-top: .4em;
40 | padding: .6em;
41 | font-size: 100%;
42 | }
43 |
44 | .menu {
45 | background-color: white;
46 | box-sizing: border-box;
47 | border: 1px solid black;
48 | width: 10em;
49 | }
50 |
51 | .menu ul, .menu ol {
52 | list-style: none;
53 | padding: 5px;
54 | margin: 0;
55 | }
56 |
57 | .menu-selected {
58 | color: white;
59 | background-color: #aaa;
60 | }
61 |
62 | input {
63 | box-sizing: border-box;
64 | width: 10em;
65 | }
66 |
67 | h1.docs,
68 | h2.docs,
69 | h3.docs,
70 | h4.docs,
71 | h5.docs {
72 | font-weight: 500;
73 | margin: 1em 0;
74 | text-transform: none;
75 | color: #000;
76 | clear: both;
77 | }
78 |
79 | h1.docs { font-size: 2.8em; margin-top: .1em; text-transform: uppercase; }
80 | h2.docs { font-size: 2.2em; margin-top: 1.5em; border-top:1px solid #ddd; padding-top: .6em; float:none; }
81 | h3.docs { font-size: 1.6em; margin-top: 1.5em; margin-bottom: .5em; }
82 | h4.docs { font-size: 1.4em; margin-top: 1.5em; }
83 |
84 | p.docs,
85 | p.docs-intro,
86 | ol.docs,
87 | ul.docs,
88 | p.docs-note,
89 | dl.docs {
90 | margin: 1em 0;
91 | font-size: 1em;
92 | }
93 |
94 | ul.docs,
95 | ol.docs {
96 | padding-bottom: .5em;
97 | }
98 | ol.docs li,
99 | ul.docs li {
100 | margin-bottom: 8px;
101 | }
102 | ul.docs ul,
103 | ol.docs ul {
104 | padding-top: 8px;
105 | }
106 | .docs code {
107 | font-size: 1.1em;
108 | }
109 |
110 | p.docs strong {
111 | font-weight: bold;
112 | }
113 |
114 | .docs-note {
115 | background-color: #FFFAA4;
116 | }
117 | .docs-note p,
118 | .docs-note pre,
119 | p.docs-note {
120 | padding: .5em;
121 | margin: 0;
122 | }
123 |
124 |
125 | /**
126 | * prism.js default theme for JavaScript, CSS and HTML
127 | * Based on dabblet (http://dabblet.com)
128 | * @author Lea Verou
129 | */
130 |
131 | code[class*="language-"],
132 | pre[class*="language-"] {
133 | color: black;
134 | text-shadow: 0 1px white;
135 | font-family: Consolas, Monaco, 'Andale Mono', monospace;
136 | direction: ltr;
137 | text-align: left;
138 | white-space: pre;
139 | word-spacing: normal;
140 | font-size: 0.8em;
141 |
142 | -moz-tab-size: 4;
143 | -o-tab-size: 4;
144 | tab-size: 4;
145 |
146 | -webkit-hyphens: none;
147 | -moz-hyphens: none;
148 | -ms-hyphens: none;
149 | hyphens: none;
150 | }
151 |
152 | @media print {
153 | code[class*="language-"],
154 | pre[class*="language-"] {
155 | text-shadow: none;
156 | }
157 | }
158 |
159 | /* Code blocks */
160 | pre[class*="language-"] {
161 | padding: 1em;
162 | margin: .5em 0;
163 | overflow: auto;
164 | }
165 |
166 | :not(pre) > code[class*="language-"],
167 | pre[class*="language-"] {
168 | background: #f5f2f0;
169 | }
170 |
171 | /* Inline code */
172 | :not(pre) > code[class*="language-"] {
173 | padding: .1em;
174 | border-radius: .3em;
175 | }
176 |
177 | pre[class*="language-"] {
178 | padding: 1em;
179 | margin: 0;
180 | margin-bottom: 1em;
181 | }
182 |
183 |
--------------------------------------------------------------------------------
/fonts/icomoon.eot:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filamentgroup/a-font-garde/8f324db7b1a35d360b7fb115c023449dcf3c988e/fonts/icomoon.eot
--------------------------------------------------------------------------------
/fonts/icomoon.svg:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Generated by IcoMoon
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/fonts/icomoon.ttf:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filamentgroup/a-font-garde/8f324db7b1a35d360b7fb115c023449dcf3c988e/fonts/icomoon.ttf
--------------------------------------------------------------------------------
/fonts/icomoon.woff:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filamentgroup/a-font-garde/8f324db7b1a35d360b7fb115c023449dcf3c988e/fonts/icomoon.woff
--------------------------------------------------------------------------------
/fonts/png/hamburger.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/filamentgroup/a-font-garde/8f324db7b1a35d360b7fb115c023449dcf3c988e/fonts/png/hamburger.png
--------------------------------------------------------------------------------
/lib/faceoff.js:
--------------------------------------------------------------------------------
1 | (function( win, undefined ) {
2 | "use strict";
3 |
4 | var doc = document,
5 | head = doc.head || doc.getElementsByTagName( "head" )[ 0 ] || doc.documentElement,
6 | style = doc.createElement( "style" ),
7 | rule = "@font-face { font-family: 'webfont'; src: 'https://'; }",
8 | res = false,
9 | blacklist = (function() {
10 | var ua = win.navigator.userAgent.toLowerCase(),
11 | wkvers = ua.match( /applewebkit\/([0-9]+)/gi ) && parseFloat( RegExp.$1 ),
12 | webos = ua.match( /w(eb)?osbrowser/gi ),
13 | wppre8 = ua.indexOf( "windows phone" ) > -1 && win.navigator.userAgent.match( /IEMobile\/([0-9])+/ ) && parseFloat( RegExp.$1 ) >= 9,
14 | oldandroid = wkvers < 533 && ua.indexOf( "Android 2.1" ) > -1;
15 |
16 | return webos || oldandroid || wppre8;
17 | }()),
18 | sheet;
19 |
20 | style.type = "text/css";
21 | head.insertBefore( style, head.firstChild );
22 | sheet = style.sheet || style.styleSheet;
23 |
24 | if ( !!sheet && !blacklist ) {
25 | try {
26 | sheet.insertRule( rule, 0 );
27 | res = sheet.cssRules[ 0 ].cssText && ( /webfont/i ).test( sheet.cssRules[ 0 ].cssText );
28 | sheet.deleteRule( sheet.cssRules.length - 1 );
29 | } catch( e ) { }
30 | }
31 | if( res ) {
32 | var html = document.getElementsByTagName( "html" )[ 0 ];
33 | html.setAttribute( "class", ( html.getAttribute( "class" ) ? html.getAttribute( "class" ) + " " : "" ) + "supports-fontface" );
34 | }
35 | }( this ));
--------------------------------------------------------------------------------
/lib/modernizr.fontface-generatedcontent.js:
--------------------------------------------------------------------------------
1 | /* Modernizr 2.7.1 (Custom Build) | MIT & BSD
2 | * Build: http://modernizr.com/download/#-fontface-generatedcontent-cssclasses-teststyles-cssclassprefix:supports!
3 | */
4 | ;window.Modernizr=function(a,b,c){function w(a){j.cssText=a}function x(a,b){return w(prefixes.join(a+";")+(b||""))}function y(a,b){return typeof a===b}function z(a,b){return!!~(""+a).indexOf(b)}function A(a,b,d){for(var e in a){var f=b[a[e]];if(f!==c)return d===!1?a[e]:y(f,"function")?f.bind(d||b):f}return!1}var d="2.7.1",e={},f=!0,g=b.documentElement,h="modernizr",i=b.createElement(h),j=i.style,k,l=":)",m={}.toString,n={},o={},p={},q=[],r=q.slice,s,t=function(a,c,d,e){var f,i,j,k,l=b.createElement("div"),m=b.body,n=m||b.createElement("body");if(parseInt(d,10))while(d--)j=b.createElement("div"),j.id=e?e[d]:h+(d+1),l.appendChild(j);return f=["",'"].join(""),l.id=h,(m?l:n).innerHTML+=f,n.appendChild(l),m||(n.style.background="",n.style.overflow="hidden",k=g.style.overflow,g.style.overflow="hidden",g.appendChild(n)),i=c(l,a),m?l.parentNode.removeChild(l):(n.parentNode.removeChild(n),g.style.overflow=k),!!i},u={}.hasOwnProperty,v;!y(u,"undefined")&&!y(u.call,"undefined")?v=function(a,b){return u.call(a,b)}:v=function(a,b){return b in a&&y(a.constructor.prototype[b],"undefined")},Function.prototype.bind||(Function.prototype.bind=function(b){var c=this;if(typeof c!="function")throw new TypeError;var d=r.call(arguments,1),e=function(){if(this instanceof e){var a=function(){};a.prototype=c.prototype;var f=new a,g=c.apply(f,d.concat(r.call(arguments)));return Object(g)===g?g:f}return c.apply(b,d.concat(r.call(arguments)))};return e}),n.fontface=function(){var a;return t('@font-face {font-family:"font";src:url("https://")}',function(c,d){var e=b.getElementById("smodernizr"),f=e.sheet||e.styleSheet,g=f?f.cssRules&&f.cssRules[0]?f.cssRules[0].cssText:f.cssText||"":"";a=/src/i.test(g)&&g.indexOf(d.split(" ")[0])===0}),a},n.generatedcontent=function(){var a;return t(["#",h,"{font:0/0 a}#",h,':after{content:"',l,'";visibility:hidden;font:3px/1 a}'].join(""),function(b){a=b.offsetHeight>=3}),a};for(var B in n)v(n,B)&&(s=B.toLowerCase(),e[s]=n[B](),q.push((e[s]?"":"no-")+s));return e.addTest=function(a,b){if(typeof a=="object")for(var d in a)v(a,d)&&e.addTest(d,a[d]);else{a=a.toLowerCase();if(e[a]!==c)return e;b=typeof b=="function"?b():b,typeof f!="undefined"&&f&&(g.className+=" supports-"+(b?"":"no-")+a),e[a]=b}return e},w(""),i=k=null,e._version=d,e.testStyles=t,g.className=g.className.replace(/(^|\s)no-js(\s|$)/,"$1$2")+(f?" supports-js supports-"+q.join(" supports-"):""),e}(this,this.document);
--------------------------------------------------------------------------------
/markup-image.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Icon Fonts
7 |
8 |
39 |
40 |
41 | Fallback to Image
42 | Fixed colors, must know the size of the glyph, requires `background-size` to resize.
43 |
44 |
45 | Menu
46 |
47 |
48 | HTML
49 |
50 | <span class="icon-fallback-img">
51 | <span class="icon icon-hamburger" aria-hidden="true"></span>
52 | <span class="text">Menu</span>
53 | </span>
54 |
55 |
56 | Failed experiments:
57 |
58 | In an attempt to remove reliance on :before/:after, we could just put the icon character in the <span class="icon">
content. But in order to replace the content with a fallback glyph we need it in CSS.
59 |
60 |
61 |
62 |
63 |
66 |
67 |
--------------------------------------------------------------------------------
/markup-tests.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Icon Fonts
7 |
8 |
44 |
45 |
46 | Bad Examples (For Testing Only)
47 | Decorative Icon without Fallback (Nested Text)
48 | If you nest the text, the icon may confuse screen readers (unable to use aria-hidden).
49 |
50 |
51 |
52 |
55 |
56 |
--------------------------------------------------------------------------------
/markup.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 | Icon Fonts
7 |
8 |
9 |
10 |
71 |
72 |
73 |
88 |
89 |
90 |
Use Case #1: Critical Icons with Fallback Text
91 |
Doesn’t show supplementary text, but must show fallback text without the icon. Supplementary text is still available to screen readers.
92 |
93 |
94 |
95 |
96 | Twitter
97 |
98 |
99 |
100 |
101 | Facebook
102 |
103 |
104 |
105 |
106 | Google Plus
107 |
108 |
109 |
110 |
111 | RSS Feed
112 |
113 |
114 |
HTML
115 |
116 | <span class="icon-fallback-text">
117 | <!-- requires an element for aria-hidden -->
118 | <span class="icon icon-twitter" aria-hidden="true"></span>
119 | <span class="text">Twitter</span>
120 | </span>
121 |
122 |
123 |
Failed experiments:
124 |
125 | Eliminate the span element and clip to just show the icon. Requires us to know the width of each glyph. It’s better to just keep the span.
126 |
127 |
128 |
Use Case #2: Critical Icons with Fallback Icon
129 |
Doesn’t show supplementary text, must show fallback icon without the primary icon. This can be a background image or a Unicode glyph. Supplementary text is still available to screen readers.
130 |
131 |
Fallback to Glyph
132 |
133 |
134 | Menu
135 |
136 |
137 |
Warning: In browsers that don’t support :before/:after (IE6-7), fallback is text.
138 |
139 |
HTML
140 |
141 | <span class="icon-fallback-glyph">
142 | <span class="icon icon-hamburger" aria-hidden="true"></span>
143 | <span class="text">Menu</span>
144 | </span>
145 |
146 |
147 |
Use Case #3: Decorative Icon without Fallback
148 |
Shows supplementary text, does not need fallback experience without the icon.
149 |
150 |
151 | Share on Twitter (Sibling Text)
152 |
153 |
HTML
154 |
155 | <span class="icon icon-twitter" aria-hidden="true"></span>
156 | Share on Twitter (Sibling Text)
157 |
158 |
159 |
160 |
161 |
170 |
171 |
172 |
173 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "afontgarde",
3 | "version": "0.1.6",
4 | "description": "The safest way to use font icons.",
5 | "repository": {
6 | "type": "git",
7 | "url": "git://github.com/filamentgroup/a-font-garde.git"
8 | },
9 | "author": "Filament Group c/o Zach Leatherman",
10 | "license": "MIT",
11 | "bugs": {
12 | "url": "https://github.com/filamentgroup/a-font-garde/issues"
13 | },
14 | "homepage": "https://github.com/filamentgroup/a-font-garde",
15 | "devDependencies": {
16 | "grunt": "~0.4.5",
17 | "grunt-bytesize": "~0.1.1",
18 | "grunt-contrib-concat": "~0.5.1",
19 | "grunt-contrib-jshint": "~0.11.0",
20 | "grunt-contrib-watch": "~0.6.1",
21 | "grunt-gh-pages": "~0.10.0",
22 | "grunt-replace": "~0.8.0",
23 | "matchdep": "~0.3.0"
24 | },
25 | "config": {
26 | "cssprefix": "supports-",
27 | "iconclass": "icon"
28 | },
29 | "dependencies": {
30 | "fontfaceonload": "^0.1.6"
31 | }
32 | }
33 |
--------------------------------------------------------------------------------
/src/afontgarde.tmpl.css:
--------------------------------------------------------------------------------
1 | .icon-fallback-text .icon {
2 | display: none;
3 | }
4 | /*
5 | ADDED BY afontgarde.js:
6 | Note: sure .FONT_NAME comes first for adjoining classes bug in IE7.
7 |
8 | .FONT_NAME.{{cssprefix}}generatedcontent .icon-fallback-text .icon {
9 | display: inline-block;
10 | }*/
11 |
12 | .icon-fallback-img .text,
13 | .icon-fallback-glyph .text/*,
14 | ADDED BY afontgarde.js:
15 | Note: sure .FONT_NAME comes first for adjoining classes bug in IE7.
16 |
17 | .FONT_NAME.{{cssprefix}}generatedcontent .icon-fallback-text .text*/ {
18 | /* visually hide but accessible (h5bp.com) */
19 | clip: rect(0 0 0 0);
20 | overflow: hidden;
21 | position: absolute;
22 | height: 1px;
23 | width: 1px;
24 | }
25 |
26 | /* Careful, don’t use adjoining classes here (IE7) */
27 | .{{cssprefix}}no-generatedcontent .icon-fallback-glyph .text {
28 | clip: auto;
29 | overflow: visible;
30 | position: static;
31 | height: auto;
32 | width: auto;
33 | }
34 | /*
35 | ADDED BY afontgarde.js:
36 | .FONT_NAME .icon-fallback-glyph .icon:before {
37 | // inherit for font-size, line-height was not working on IE8
38 | font-size: 1em;
39 | font-size: inherit;
40 | line-height: 1;
41 | line-height: inherit;
42 | }*/
43 | .icon-fallback-img .{{iconclass}} {
44 | display: inline-block;
45 | }
46 | .icon-fallback-img .{{iconclass}}:before {
47 | content: "";
48 | }
49 | /* The img fallback version is not as reliable since it does not check to make sure the fontloaded font has loaded. If we did add the .fontloaded class, it would unnecessarily request the fallback image. */
50 | .{{cssprefix}}fontface.{{cssprefix}}generatedcontent .icon-fallback-img .{{iconclass}} {
51 | background-image: none;
52 | }
--------------------------------------------------------------------------------
/src/afontgarde.tmpl.js:
--------------------------------------------------------------------------------
1 | /*
2 | * A Font Garde
3 | */
4 |
5 | ;(function( w ) {
6 |
7 | var doc = w.document,
8 | ref,
9 | css = ['.FONT_NAME.{{cssprefix}}generatedcontent .icon-fallback-text .icon { display: inline-block; }',
10 | '.FONT_NAME.{{cssprefix}}generatedcontent .icon-fallback-text .text { clip: rect(0 0 0 0); overflow: hidden; position: absolute; height: 1px; width: 1px; }',
11 | '.FONT_NAME .icon-fallback-glyph .icon:before { font-size: 1em; font-size: inherit; line-height: 1; line-height: inherit; }'];
12 |
13 | function addEvent( type, callback ) {
14 | if( 'addEventListener' in w ) {
15 | return w.addEventListener( type, callback, false );
16 | } else if( 'attachEvent' in w ) {
17 | return w.attachEvent( 'on' + type, callback );
18 | }
19 | }
20 |
21 | // options can be a string of glyphs or an options object to pass into FontFaceOnload
22 | AFontGarde = function( fontFamily, options ) {
23 | var fontFamilyClassName = fontFamily.toLowerCase().replace( /\s/g, '' ),
24 | executed = false;
25 |
26 | function init() {
27 | if( executed ) {
28 | return;
29 | }
30 | executed = true;
31 |
32 | if( typeof FontFaceOnload === 'undefined' ) {
33 | throw 'FontFaceOnload is a prerequisite.';
34 | }
35 |
36 | if( !ref ) {
37 | ref = doc.getElementsByTagName( 'script' )[ 0 ];
38 | }
39 | var style = doc.createElement( 'style' ),
40 | cssContent = css.join( '\n' ).replace( /FONT_NAME/gi, fontFamilyClassName );
41 |
42 | style.setAttribute( 'type', 'text/css' );
43 | if( style.styleSheet ) {
44 | style.styleSheet.cssText = cssContent;
45 | } else {
46 | style.appendChild( doc.createTextNode( cssContent ) );
47 | }
48 | ref.parentNode.insertBefore( style, ref );
49 |
50 | var opts = {
51 | timeout: 5000,
52 | success: function() {
53 | // If you’re using more than one icon font, change this classname (and in a-font-garde.css)
54 | doc.documentElement.className += ' ' + fontFamilyClassName;
55 |
56 | if( options && options.success ) {
57 | options.success();
58 | }
59 | }
60 | };
61 |
62 | // These characters are a few of the glyphs from the font above */
63 | if( typeof options === "string" ) {
64 | opts.glyphs = options;
65 | } else {
66 | for( var j in options ) {
67 | if( options.hasOwnProperty( j ) && j !== "success" ) {
68 | opts[ j ] = options[ j ];
69 | }
70 | }
71 | }
72 |
73 | FontFaceOnload( fontFamily, opts );
74 | }
75 |
76 | // MIT credit: filamentgroup/shoestring
77 | addEvent( "DOMContentLoaded", init );
78 | addEvent( "readystatechange", init );
79 | addEvent( "load", init );
80 |
81 | if( doc.readyState === "complete" ){
82 | init();
83 | }
84 | };
85 |
86 | })( this );
--------------------------------------------------------------------------------
/src/banner:
--------------------------------------------------------------------------------
1 | /*! <%= pkg.title || pkg.name %> - v<%= pkg.version %> - <%= grunt.template.today("yyyy-mm-dd") %>
2 | <%= pkg.homepage ? " * " + pkg.homepage : "" %>
3 | * Copyright (c) <%= grunt.template.today("yyyy") %> <%= pkg.author %>
4 | * <%= pkg.license %> License */
5 |
6 |
--------------------------------------------------------------------------------