Icons
This guide is deprecated refer to UI kit for the latest information.
What's the deal with "old and new" Icons? ¯\_(ツ)_/¯
If you're reading this, it is very likely that you, or someone else, has pointed out to you that you should be replacing old icons with new icons - and you're a bit confused as to what that means.
When this project started, all of our icons consisted of using the existing SVG spritesheet based icons being used by the web frontend
project. Rather than loading entire spritesheets multiple times, we have devised scripts that download the different spritesheets from our assets server, and then split them out, and write them to individual svg files per icon. This is how the majority of icons in the app have worked.
We are now switching over to using font based icons, instead of using SVGs, as using fonts will help us reduce our bundle size, as well as speeding up our loading times.
At present, we have 3 sets of icons in the app:
- SVG icons (monochrome and coloured, non-name-spaced)
- Font icons (monochrome only, name-spaced)
- SVG icons (coloured only, name-spaced)
The reason why we have a separate set of colour-only SVG icons is that font based icons do not support multiple colours, so we have to still use SVGs for some of them.
You will have noticed that there is also a mention of name-spacing vs non-name-spacing. What's going on there is that our old SVG icons were imported from separate sheets, but did not inherit their spritesheets name, which were divided by category of icons. So if there is a misc
spritesheet and a sports
spritesheet...and both have an icon called tennis
, we had two icons of the same name, causing a name collision and potentially loading the wrong icon.
To resolve that, we properly name-spaced our font icons, to include the respective category they belong to, so now we would have both misc-tennis
and sports-tennis
, for example.
What are we trying to achieve here?
We want to go from having this in our app bundle:
- Old SVG icons, used for both single and multi color icons (Bad performance)
- New font icons, used just for single color icons (Good performance)
- New SVG icons, used for just multi color icons (Bad performance, but fewer of them)
To being replaced with having:
- Font icons, used just for single color icons
- SVG icons, used for just multi color icons
The only way to do that is to ensure that all instances of using the old non-name-spaced SVG icons are replaced with the use of the new icons, which employ name-spacing in the individual icons' names. e.g. tennis
gets replaced by sports-tennis
, or racing
gets replaced by nav-racing
where appropriate. This is because multiple different icon sprite sheet from which the icons are made in our assets server may have the same individual icon name that is the same across different sprite sheets, resulting in the wrong icon being loaded by our app at runtime.
Once that is done, the next goals are to optimise our remaining SVG icons by:
- Importing only the required icons at startup for the home screen; and
- Load icons dynamically when needed, i.e. only load the icons for a certain screen, when navigating to said screen.
We currently export every single icon, at startup, even ones that appear on screens that the user may rarely navigate to, which slows our app down somewhat.
With our new font icons, that is much less of a concern as we can load all of the icons together quicker and with a smaller footprint than entire SVG files, however we do still have coloured SVG icons we use, and those only appear in some instances in the app. We would want to import those and load them in the app only on the screens when needed, rather than at app startup. This is something we yet have to do, once all the old apps are removed.
There are further considerations of converting all the color-only SVGs to PNGs, as that would increase performance even further (due to PNGs being able to be cached and rendering faster, at a small cost of increased memory use) - but that won't be until after the current conversion of SVG icons to fonts is complete.
Where are they?
You can see all of the old SVG icons that we currently have in the app, under their respective brand folder, e.g. ladbrokes/assets/<category name>-icons.js
. All of these are then exported under a single Icons
variable, in the index.ts
file in the <brand>/assets
folder.
For the font icons and coloured-only SVGs, they are in e.g. ladbrokes/assets/font-icons
and ladbrokes/assets/svg-icons
.
The coloured-only SVG icons are exported the same exact way as the old SVG icons as there is no other difference except for the name-spacing for each exported SVG component.
For the font icons, you will see that there is both a <brand name>-icon-font.json
file as well as a <brand name>-icon-font.ts
file. The JSON file is what is exported currently, and what is then parsed by the <Icon>
component.
Old SVG icon files (tech debt)
These files are still in the project until we have confirmed that they are not being imported anywhere. They are located in the assets/
folder itself, in -icons.js
files, rather than being in the assets/font-icons
or assets/svg-icons
folders where the new SVG and Font icon files are.
How do I switch using the old SVG icons for the new SVG and font icons?
You are able to switch between using the old SVG or new font and new SVG icons merely by changing to the appropriate, name-spaced, value for the name
prop of the Icon
component, e.g.
<Icon name="tennis />
vs
<Icon name="sport-tennis" />
All of the icons, from all 3 sets, are imported as a list of names that resolve to the relevant font icon object or SVG icon file, by the <Icon>
component, so they should all be available.
Previewing icons in the app
We have a testing screen which allows you to preview all of the Icons (both the new font icons, and old SVG icons).
To view this screen, got to the 'More' tab in the app, scroll to the bottom and tap 'View Icons'. You'll only be able to see this screen when running in the development environment.
The app shows a ?
or I get an error when trying to use the new icon in the Icon component - what do I do?
If an icon defaults to displaying the ?
symbol when previewed in the app, or you get an error when trying to use the new icon name, follow these steps:
- Check that you're using the right icon name. The default
?
icon is shown when theIcon
component can't find the icon name you've passed in thename
prop.- Check that you have the right name by referencing the JIRA ticket (if there is one), design documents you're working off of, or ask your team's designer to confirm the name is right.
- Once you have confirmed you've got the right icon name, do a project-wide code search, looking for that icon name.
- If you see the icon name inside of
<brand name>-icon-font.json
but you still see?
in the app, then the likely issue is that the SVG file doesn't exist (in one of the-svg-icons.js
files, found here or here) or the font gylph isn't inside of the font files (found here).
If an icon SVG isn't in our svg-icons
folders, or cannot be found inside of the .ttf
font files in resources/fonts/
, that is likely because we haven't imported it into react-native
.
We will need to ask the designers to create, export and publish the icons to our static assets server, and then import them into react-native
. That's covered further below in the document.
Adding icons to the app
Our icon assets are created by the design team and stored in the repository named static.
These icons are then pulled down by a set of scripts (detailed below) into our project, and moved to the relevant project folder for us to be able to use them in React Native.
Generating and Importing new SVG and Font icons
To get any new icons, especially ones missing from our project, we have to fetch them from the static
project.
Checking that the icon asset exists first
The first thing you want to make sure is that the actual SVG icon assets (sprites) exists at all in the static
project repo. The Ladbrokes ones should be found here, whereas the Neds ones should be here.
Here you will see various sprite sheets, that are used in the frontend
project (our websites), and correspond to the categories of icons that each individual icon belongs to (e.g. sports-tennis
is inside of svg-sports-icon.svg. The individual icon SVG files themselves are inside of the src/
folder found here, in sub-folders that match the sprite-sheet category.
These individual SVG icons, if they are coloured icons, are what we fetch via a script into react-native
, but we use the newer naming convention of prefixing each icon's actual name, with the category it belongs to, to avoid any potential name double-ups, (e.g. racing-horse-racing
and racing-speed-map-horse-racing
, where racing-
is the common category between those two).
For font icons, the files in the static
project repo are kept separately in the rn
folder (e.g. here, where you will find a single SVG sprite sheet, named just after the category of icons it contains.
Generating and Publishing new icon assets on the static assets server
This is outside of the scope of this guide. To see how to generate icon assets, both SVG and Font icons (for our native apps specifically), see the documentation for the static
project here in the README.md file.
Once an icon asset has been generated in the static
repo, it will need to be committed (saved) in the repo, and the changes need to be pushed via a pipeline, to publish the new asset to the website, via our AWS S3 bucket. It is only at this point that we can import and use the icons in both frontend
and react-native
.
Importing new icons assets into the app
To import any new or updated icons that have been published to our static assets server, you will need to use a dedicated script in our react-native
project. The script itself can be found here if you would like to take a look, although you do not need to know how it works to use it.
To generate both of the new icon types (font and colored-only SVGs), use the following commands by opening a terminal at the root directory of our project.
yarn generate:icons
This command will grab any new or updated SVG sprite sheets and Font files that contain new/updated icons for us to use in react-native
. The command should confirm if the fetching was successful, and display any errors - be sure to read the console outputs.
Once the command is finished, you should be able to use any new or updated icons simply by using the appropriate icon name by passing it to the Icon
components name
prop.
If any new icons have been fetched, or any existing icons have been updated, there will be several modified files in your source control - be sure to commit these in any MR that you are working on that's Icon related to ensure that the changes are picked up and in the app next time new builds are created.
Be sure to restart the Metro bundler and completely re-build the native app bundle to see the changes locally first.
A note on icon font file and react native linking
We are currently using the react-native-vector-icons library in order to display the font icons and the coloured SVG icons.
There is currently an issue with how the library does it's auto-linking, whereby on new installs it will most likely try to add all of the bundled icon packs it comes with to the XCode project files. Remove those changes from git before committing them when you're starting from a completely fresh workspace.
Styling font icons and sizing difference between them and SVG icons
NOTE: There is one quirk when dealing with font icons vs SVG icons, and that is sizing.
When you are using an SVG icon, you can use the width
and height
props to adjust it's size. However, when using a font icon, because it's a font, you will have to in most instances switch to using the size
prop.
Have a look at the Icon component implementation for better understanding of how it works.
So for example if you were using
<Icon name="tennis" height={14} width={12} />
You will now have to use
<Icon name="sport-tennis" size={12} />
Using the height
and width
will override the size
prop if used with a font icon. Using the theme.font.size.<size value>
theme variables would work as well, as they resolve to number values themselves.
If in doubt, refer to the Icon.tsx
file and the React Native Vector Icons library's docs.
When replacing an "old" icon (SVG) with a "new" one (coloured SVG or monochrome font icon), the icon should still be the same size as before, unless part of your job is specifically to change its size. This may mean you'll have to alter the new icons size props. Visual parity is more important that value parity in this case.
Default icon size
Our default icon size is 20
, and in most cases, that is the size that the icon should be. In our Icon
component, we set the value of size
to 20
by default, so unless you need the icon to be a different size (check with the designer unless there's a design doc handy for the screen/component your working on), you should be able to omit the size
prop and the size will automatically default to 20px
.
Old and new icons may render differently!
It's important to check how the UI has changed when switching from using the old SVG icons to using the new font icons, as the new icons have been normalised size-wise, whereas the old SVG icons had some discrepancies in width and height of the canvas between them.
As such, some custom styling may have been needed at the component level, to adjust for these oddities, that may suddenly no longer be necessary when using the more uniformly sized font icons, leading to the UI changing.
You cannot rely on snapshot tests to validate this, and should "eyeball" the UI changing.
It should be noticeably different when switching icons, if a sizing issue is in play. If everything looks the same, then carry on.
Adding a new icon font SET to the app (e.g. when adding a new brand)
NOTE: YOU CAN SKIP THESE STEPS IF YOU ARE JUST UPDATING AN EXISTING FONT FILE AND NOT ADDING AN ENTIRE BRAND NEW FONT SET
If you are just updating which fonts are included on an existing font set, skip this section
There are some manual steps that must be done when importing new icon font files into the project, and this only applies to iOS. For Android, just placing the files within the correct folders is enough, and our scripts do that automatically.
As for iOS, while the generateIcons.js
script that you run with yarn generate:icons
will grab the font icon files from the static
repository, you will still need to add these generated files to the ReactNative project in XCode manually, as the project configuration (.pbxproj) files need to know that you are associating the new font icon files with the project, for when the app gets built and the app bundle is created.
iOS
Follow these steps:
- Familiarise yourself with the react-native-vector-icon docs, as this is the library that enables us to use the font icons in react native. There you will find general instructions on how to add a new icon font to react native, and it should be your starting point. If any issues occur, looking at the project's issues section should be your first stop.
- NOTE: As it currently stands, the library will more than likely try to include ALL of the bundled icon sets that it comes with, by default, to the iOS project when you do a
pod install
. Check the changes in git and revert any lines added toproject.pbxproj
file that add any icon sets other than the icons we are adding - you should be able to figure out which ones aren't our icon font file.- TODO: We should find a way to disable this until the library author gives us the option to exclude them...but so far we haven't figured out a way to do it)
- The library is already set up to work with our project, so the only thing you should be doing here is adding and referencing any new
ttf
files that are being added to the project (e.g. any new brand icon sets when they become available, like betstar-icon-font.ttf). - Open Xcode, and open up the
Build Phases
section in the middle panel for the relevant target (the Xcode name for the different versions of the same app, i.e. a different brand) you want to add the newttf
file for- Again, you won't need to do any of this for existing font files, unless their names change - as you're only referencing where the files being imported by the script are, and letting Xcode know to associate them with the project. If you are adding brand new files with a different name, read on.
- Under
Copy Bundle Resources
, press the+
button, thenAdd Other...
and navigate to the{BRAND}-icon-font.ttf
file located in the{BRAND}/assets/font-icons
folder. Add thettf
file. - Make sure that
Destination: [x] Copy items if needed
is checked and thatCreate groups
is selected underAdded folders
. - Switch to the
Info
tab in Xcode in the same panel, for the same target. There, look for theFonts provided by application
. Here you are editing theInfo.plist
file. If the key doesn't already exist, add it, and once added or if it already exists - hit the>
button to expand the drop down list, and add a new item. You will need to add the name of the font here. It should be the same exact name and file extension as the font file itself: e.g. if the font file is calledlabrokes-icon-font.ttf
, thenladbrokes-icon-font.ttf
is what you should be adding. MAKE SURE YOU SPELL IT RIGHT! - Recompile the project. That's it - the fonts should now be usable in iOS.
Android
The yarn generate:icons
script will automatically grab the latest font icon files, and place them into the relevant android folders.
- The location is
android>app>src>main>assets>fonts
- There you will find the corresponding
<brand>-icon-font.ttf
file for brands that have icon fonts.
Using the newly added font icon set
When you use the above-mentioned generateFontIcons.js
script - the relevant font icon component should be auto-generated for you with the correct params passed into the createIconSet
method that references the right files for iOS and Android. See the react-native-vector-icon docs for more details.
If you are adding a brand new icon set that isn't replacing an existing one, and has a different name you will have to expand the logic in our Icon
component to parse through the .json
glyphmap file for the new ttf
file that would also have been generated when you imported the new font icons through the generateFontIcons.js
script. You can look at Icon
to see an example of how the existing font icons are being implemented.
Icon name enum
At the moment, we are using the string names of the icons directly, however we also have at our disposal an enum
of all of the icon names. The enum
is auto-generated in a ts
file along with the rest of the font icon files.
The enums are located in a .ts
file where the font icons are, e.g. ladbrokes/assets/font-icons/ladbrokes-icon-font.ts
.
Note, at present, the Icon
font is not written to accept the enum
s directly, and it still expects the actual string name of the icons. This note is just here to clear up confusion as to what that file is doing there.