Makeanico is a progressively enhanced web app that allows anyone to create a 16x16 favicon.ico graphic. You can access it by waking the sleeping dino.
Makeanico was originally created as a submission for the 2016 10K Apart competition.
The "base" functionality of Makeanico is the ability to create a 16x16 favicon and export it in .ico, .svg and .png formats. This base functionality is achieved with an initial load of less than 10kB. Approximately 2kB of CSS styles are used to graphically enhance the experience for sighted users. This is done by styling a semantic HTML
<input type="checkbox"> elements to create a "WYSIWYG" graphics editor.
Without the WYSIYG CSS styles we lose that "what you see is what you get" feature but powered by semantic HTML the raw experience is as functional with or without styles.
npm start to run the server in production mode with GZIP and minification enabled. Host over https.
In accordance with the Rules & Regulations of the competition Makeanico keeps initial page weight under 10kB. I use the Chrome Developer Tools to get an idea of the page weight. Safari and Firefox seem to display uncompressed file sizes even though GZIP compressions is in fact being used.
That's the initial load. If you include
init.min.js which is lazy-loaded right off the bat for users whose scripts can cut the mustard then we're at:
Note: Sizes include header weight
Again, it is my understanding that given that the init script is only loaded as needed (if it passes the
doEnhancments test) than in doesn't count. But I want to demonstrate that even if it does I calculate the weigh in at under 10kB.
<script> var doEnhancments = document.addEventListener ? true : false; if(doEnhancments) document.write('<script id="scripts__init" src="/assets/js/init.min.js"><\/script>'); </script>
The favicon.png, which is not included in the weight, weighs 266 bytes in Finder. So even if you count that it is still under 10kB! The service worker, which is loaded
if 'serviceWorker' in navigator weighs 324 bytes GZIPed. I don't think the weight of lazily loaded service workers counts though…
Once a user begins interacting with the art–board or choosing a fill color enhancements are loaded as needed. These include the:
- live preview
- keyboard shortcut
- accessibility preferences
Some Enhancements are fetched for "dirty" art–boards. If there is art on the canvas, editing enhancements are loaded. Since the app has previously been visited, these assets are likely loaded from cache by the browser cache or the service worker. Any dirty artboard is not the initial page load so it is ok for these pages to creep above 10kB.
The more pixels you draw, the longer your URL gets, and the larger the size of the
index.html gets too! That's ok though because only the "blank canvas" homepage counts as the initial load. Pretty much everything except the HTML source is going to be served by the browser cache if possible. Even the HTML source itself of return visits is cached offline by the service worker. This means that not only can page visits weight 0.0kB but also that JavaScipt users performing asynchronous actions that don't need to communicate with the server (import, export, post) can enjoy an offline editing experience.
The larger initial page weights of lazy–URLs I have counted to dirty art–boards are over 10kB. That's with the service worker and browser cache off and includes the initial weight plus all the lazy–loaded enhancements. To reproduce this visit a dirty art–board with every color filled.
You can also reproduce this by importing a photo with edge–to–edge to color. From a mobile device snap a photo to import your art to the art–board.
The Accessibility Preferences are lazy loaded in
localStorage capable environments. They lazily add about 730 bytes of CSS and 1.6kB of scripts if
localStorage dictates they should be loaded. For example, if the Font Size preference is changed from the default value, a little CSS and some scripts will lazily be loaded to update the user interface is response to user preferences. You can also choose a typeface like OpenDyslexic and Fira. Of course the web fonts are heavy and lazily loaded only if requested by the user.
Color the Browser
Browsers that support the
apple-mobile-web-app-status-bar-style tag(s) will visually convey the selected color within the browser interface.
History is supported in both the base and enhanced experiences. The HTML5 History API is used to leverage Undo, Save, Bookmark, and Share features by pushing the graphic state to the URL. As you update your favicon the URL is updated accordingly by pushing the pixel data into the URL as URL parameters. Therefore, each unique favicon naturally has its own unique URL! You can bookmark your favicon to return and work on it later. Use the browser back and forward buttons to navigate the timeline of your edits.
Live Favicon Preview
<a download> so clicking them will download the current artwork as an SVG.
The favicon of the page itself is also asynchronously updated! So in browsers that support dynamic favicons, you'll see your edits reflected there as well.
Accessibility is water. We are all in need of and deserving of it. Working within the constraints of web standards, we construct our favicon editor from a semantic
<input type="checkbox"> elements. With no additional expense, these elements inherit the implicit accessibility of native browser inputs. Each pixel or "cell" consists of an
<input type="checkbox>" element and a corresponding label. These native elements provide keyboard focus, selection, and screen reader support along with compatibility with any assistive technology that integrate with native HTML
Our Fill Selected Cells component allows the user to input a color in several different ways. This presents convenience and accessibility to our users. Some users may prefer dictation and opt for the standard text input method. The text input accepts a hex color or standard CSS color name as input. In supported browsers, users will be presented with an option to use a colorpicker input. Finally, users can input or adjust colors using the RGB sliders.
For connivence a swatch component is lazy–loaded as needed. localStorage enabled users can save their frequently used colors for quick and easy access. Built upon a fieldset of radio inputs, the swatch panel leverages implicit accessibility.
The HTML5 datalist component is also used to enhance accessibility and usability. Once you set focus to the hex text input area a datalist component containing a list of CSS color names is lazy–loaded. Users in modern browsers that support the datalist component will get typeahead hints as they type in the hex input area. Browsers that do not support datalist will display a standard select component still allowing the user to chose between typing in a value or selecting one.
Users with localStorage enabled also are presented with Accessibility Preferences. At the
/preferences page they will be presented with Legibility, Typeface, Contrast, Animation and Visibility components that allow them to manage related preferences. These preferences will be stored in
localStorage and return visits will respond to user's preferences accordingly.
One of the thing I discovered through this process is that accessibility is a universal topic. It isn't just about screen readers and HTML. Accessibility applies to everything. I consider the import features not just power user features but accessibility features as well. They allow people to access their art quicker if they already have something prepared that they would like to import. This makes for a less fatiguing user experience.
Aside from the API, there are two ways to import art to the art–board.
- Upload a SVG or PNG image
- Use the PhotoShop ExtendScript
Both of these methods perform similar tasks. They take a graphic, size it down, crop it to 16x16, loop over each pixel, and direct you to the appropriate URL for your art.
Pro Tip: By slightly altering the script, entire directories can be imported which is how the World–Wide Favicons were imported.
Similar to accessibility, most of the keyboard considerations are provided by web standards and the browser. I didn't have to write any code for you to be able to easily jump around from cell to cell, input to input, or widget to widget. The semantics of the document provide that. Nevertheless, there are a few considerations and shortcuts I did make.
There are keyboard shortcuts for each of the selection tool buttons. Alt/Option + A will select all sells. Alt/Option + I will inverse the current selection. Alt/Option + D will deselect all cells. Mouse users can also click a cell and hold Alt/Option when clicking another cell to range select several cells. I do think additional keyboard considerations could be made but I'm waiting for more user testing and feedback before proceeding.
A lightweight service worker is used to provide offline support and increase performance. Users not receiving scripts will still need to post to the server, but users receiving scripts should be able to go offline and keep making their favicon.
Accessibility isn't just about color contrast and screen reader support. In an attempt to allow access to anyone, an accessible experience weeds out barriers to entry. Page weight is a barrier to entry, so we do what we can to keep the weight down and leverage the browser cache for the assets we do load.
One page experiences like MakeanIco can increase their PageSpeed score by inlining critical CSS and small scripts. However, we don't inline our enhancements. To do so would not leverage the browser cache effectively. Loading enhancements through HTTP requests allows them to be cached across all the URL endpoints of the application (of which there are many – each icon has its own URL).
ARIA is salt. So we use it sparingly and only as needed. I'd rather add seasoning later after feedback and upon request. So much of the foundation is semantic HTML that little ARIA is used or needed. Accessibly hidden text is used to allow the HTML to be semantic and audible to assistive technology while still visually appealing and contextual to sighted users.
Any icon's art is contained in the URL. Each cell, or pixel, is represented as a URL parameter. The web app uses the trendy new 8 Digit Hexadecimals to store color so to draw an icon where the first pixel is 50% red you'd use
/?c1=0xff000080. Each cell has a numeric index starting from
c1 and ending in
c255. The value should be a 6 or 8 digit hexadecimal starting in
URL parameters can be used to set the initial color input type as well as the initial fill color. For example,
?fill=0x00ffff0c&colorby=rgba. These defaults will not override an values found in the users
/make/favicon.ico endpoints that can be used to load a dynamic favicon graphics in the requested format. For example
/make/favicon.png?c23=0x0cFF00FFFF&dl=1 for an immediate download.
World–Wide Favicon Flags
In the spirit of the World–Wide Web, MakeanIco provides a public API for favicons of each country flag.
|Flag||Country Code||Edit Page||SVG Icon||PNG Icon||ICO Icon|
Note: To trigger an icon to be downloaded add a
&dl=1 URL parameter to the
Brands and Icons
I've added endpoints for some of my favorite favicons. Follow the links in the icon column to navigate to the edit screen of your favorite icons.
This web app strives for WCAG 2.0 Guidelines Level AA. Please open an issue for any accessibility issue, feedback, or concern.
Makeanico has excellent browser support but isn't without issue.
Makeanico is architected starting with an HTML–first web standards layer and is progressively enhanced. It therefore has very ubiquitous support. Even IE6 users can use the semantic form and synchronously illustrate an icon. Lynx users can read the contents of an icons artboard.
That said, there is a known issue in IE and Edge will URLs exceeding 2083 characters. This causes issues with exporting dirty art–boards in Edge. Edge is tracking an issue to resolve this so no action is being taken. Export of very dirty art–boards will land when this is addressed by Edge. That will be an exciting update!
*Does not export very dirty art–boards
The synchronous or
no-js experience should work in just about any browser so we'll take bug reports for any browser.
Scripts are only loaded if needed and pass feature tests. Polyfills are only loaded if needed and fail feature tests. Polyfills for
Promise are lazily loaded if needed. These are both needed for IE11. The fetch polyfill is needed for Safari 10.0 and iOS 10 but Safari will soon shed this polyfill.