Structuring app resources
In this article we will discuss best practices around managing assets and resources in Fuse projects.
We usually separate between resources and assets as follows:
- Assets usually come in the form of files containing content like images, videos or fonts.
- Resources define certain values that can be used throughout our app, like colors, font declarations (which acts as a reference to a font file usable in our apps) or strings with different translations.
Folder structure
We like to keep files organized according to which role they fulfill in our app. Thusly, we usually create a Resources folder and an Assets folder, where we put our resources and assets respectively. Beyond this, feel free to further organize your assets and resources in subsequent subfolders.
Example
- MyApp/
- MyApp.unoproj
- MainView.ux
- Components/
- Pages/
- Resources/
- ColorPalette.ux
- ColorTheme.ux
- Fonts.ux
- Assets/
- Fonts/
- RobotoBold.ttf
- RobotoRegular.ttf
- Images/
- DefaultAvatar.png
- SplashScreen.jpg
Resources
A resource in Fuse is a value that we associate with a key (a name), so that it can be referenced by that key instead of having to replicate the exact value in multiple places. Examples of values that we prefer to make resources for include colors, fonts, strings and even margins and paddings. Another very useful aspect of resources is that they can be overridden at any time, meaning we can use this to define color palettes / themes that we can swap out even while the app is running.
There are two ways we can define resources and while they act in a similar way, they have some subtle but important differences.
- Static resources are defined using the
ux:Global
attribute and allow us to associate a value with a key and make it available globally in our app. - Dynamic resources are defined using the
ux:Key
attribute and allow us to associate a value with a key. This makes the value available to the entire subtree of where it is defined.
Lets take a look at an example:
<float4 ux:Global="AppBackground" ux:Value="#7FDBFF" />
Here we have defined the name AppBackground
to refer to the blueish color #7FDBFF. This value can then be used like so, anywhere in our app:
<Rectangle Color="AppBackground" />
Defining static resources/globals with meaningful names like this lets us easily tweak the look and feel of our app with only a single code change, even though the color is used in many places.
A limitation of static globals is that they cannot be changed while the app is running. For that use-case, we have dynamic resources:
<Panel>
<float4 ux:Key="AppBackground" ux:Value="#7FDBFF" />
<Rectangle Color="{Resource AppBackground}" />
</Panel>
In the above example we've defined the same color, but as a dynamic resource instead. There are a couple things to notice about this snippet.
We have wrapped the Rectangle
and float4
resource in a Panel
to illustrate that the resource has to be either a sibling of the Rectangle
, or belong to one of its ancestors in order to be visible. Since we put it on the Panel
it is available to the panels descendants, or any of its descendants.
You might also have noticed that we needed to use a special binding syntax in order to access this resource: {Resource AppBackground}
. Since the resource might change while the app is running, we need to create a binding here so that our Rectangle
is notified whenever that occurs.
The
{Resource <key>}
binding also works for static resources defined usingux:Global
. It will first look for a dynamic resource defined byux:Key
, but if none is present, it will pick the static one by the same name instead.
Color palettes
When defining a color palette for our app, it can be a good idea to put it in its own file. This way we don't get a long list of color definitions cluttering our UI code, and it becomes easier to make color changes at a later stage. One detail of Fuse is that all files must define a ux:Class
in order to be included in the app. When we just want to define a bunch of resources however, we don't want to also have to define a class to contain them.
To make grouping resources more convenient, UX markup has a special tag called ux:Resources
, which is used as follows:
<ux:Resources>
<float4 ux:Global="MyApp.RedColor" ux:Value="#F44336" />
<float4 ux:Global="MyApp.PrimaryColor" ux:Value="MyApp.RedColor" />
<float4 ux:Global="MyApp.TopBarBackgroundColor" ux:Value="MyApp.PrimaryColor" />
</ux:Resources>
All resources defined inside ux:Resources
tags are automatically included in the app.
Tips on structuring color palettes
- You should define all colors as static resources using
ux:Global
. Instead of using these globals directly however, it is a good idea to instead use the{Resource <key>}
binding syntax so we can easily define various color schemes at a later stage. - It's better to use semantic names for colors. For example, instead of naming colors after their value, like
RoseGold
orMintGreen
, use names that hint to where in the app the colors should be used, likeLightBackgroundColor
,DarkTextColor
orMainAccentColor
.
Themes
The current way of creating themes in Fuse is to simply group all resources associated with the theme in an active trigger:
ColorPalette.ux
<ux:Resources>
<float4 ux:Global="Sienna" ux:Value="#a0522d" />
<float4 ux:Global="SkyBlue" ux:Value="#7ec0ee" />
<float4 ux:Global="MyApp.PrimaryColor" ux:Value="Sienna" />
</ux:Resources>
MyTheme.ux
<WhileTrue ux:Class="MyTheme" Value="true">
<float4 ux:Global="MyApp.PrimaryColor" ux:Value="SkyBlue" />
</WhileTrue>
We can then very easily use this theme in our app by adding it to our app root:
<App>
<MyTheme />
<Panel Color="{Resource MyApp.PrimaryColor}" />
</App>
Since MyTheme
inherits from the WhileTrue
trigger, it can easily be turned on and off by either data-binding its Value
property to a JavaScript
variable, or with pure UX using for example a Clicked
trigger and a Toggle
action:
<App>
<MyTheme ux:Name="myTheme" />
<Panel Color="{Resource MyApp.PrimaryColor}">
<Clicked>
<Toggle Target="myTheme" />
</Clicked>
</Panel>
</App>
Note that we here have also defined a
ux:Global
forMyApp.PrimaryColor
. This is to make sure our app has a fallback value for the case when no theme has been specified.
Fonts and text styles
Fuse supports both .otf
(OpenType) and .ttf
(TrueType) font files. In order to make a font available throughout your app you can create a static resource for it:
<Font ux:Global="RobotoBold" File="Assets/Fonts/Roboto-Bold.ttf" />
The font is then ready to be used in Text
and TextInput
objects by assigning their Font
property:
<Text Font="RobotoBold">Hello with Font</Text>
Instead of assigning your custom font to all text objects however we prefer creating a set of text styles using ux:Class
and instead use those in our app. This makes it easy to switch our fonts, text size and colors throughout our app by making changes in a single place.
<Text ux:Class="MyApp.TitleText" Font="RobotoBold" FontSize="20" Color="{Resource MyApp.TitleTextColor}" />
<Text ux:Class="MyApp.BodyText" Font="RobotoRegular" FontSize="13" Color="{Resource MyApp.BodyTextColor}" />
<StackPanel>
<MyApp.TitleText>This is a title</MyApp.TitleText>
<MyApp.BodyText>And this is some body text...</MyApp.BodyText>
</StackPanel>