Styling Xamarin.Forms apps using Cascading Style Sheets (CSS)
Xamarin.Forms supports styling visual elements using Cascading Style Sheets (CSS).
Xamarin.Forms applications can be styled using CSS. A style sheet consists of a list of rules, with each rule consisting of one or more selectors and a declaration block. A declaration block consists of a list of declarations in braces, with each declaration consisting of a property, a colon, and a value. When there are multiple declarations in a block, a semi-colon is inserted as a separator. The following code example shows some Xamarin.Forms compliant CSS:
navigationpage {
-xf-bar-background-color: lightgray;
}
^contentpage {
background-color: lightgray;
}
#listView {
background-color: lightgray;
}
stacklayout {
margin: 20;
}
.mainPageTitle {
font-style: bold;
font-size: medium;
}
.mainPageSubtitle {
margin-top: 15;
}
.detailPageTitle {
font-style: bold;
font-size: medium;
text-align: center;
}
.detailPageSubtitle {
text-align: center;
font-style: italic;
}
listview image {
height: 60;
width: 60;
}
stacklayout>image {
height: 200;
width: 200;
}
In Xamarin.Forms, CSS style sheets are parsed and evaluated at runtime, rather than compile time, and style sheets are re-parsed on use.
Note
Currently, all of the styling that's possible with XAML styling cannot be performed with CSS. However, XAML styles can be used to supplement CSS for properties that are currently unsupported by Xamarin.Forms. For more information about XAML styles, see Styling Xamarin.Forms Apps using XAML Styles.
The sample demonstrates using CSS to style a simple app, and is shown in the following screenshots:
Consuming a style sheet
The process for adding a style sheet to a solution is as follows:
- Add an empty CSS file to your .NET Standard library project.
- Set the build action of the CSS file to EmbeddedResource.
Loading a style sheet
There are a number of approaches that can be used to load a style sheet.
Note
It's not currently possible to change a style sheet at runtime and have the new style sheet applied.
XAML
A style sheet can be loaded and parsed with the StyleSheet
class before being added to a ResourceDictionary
:
<Application ...>
<Application.Resources>
<StyleSheet Source="/Assets/styles.css" />
</Application.Resources>
</Application>
The StyleSheet.Source
property specifies the style sheet as a URI relative to the location of the enclosing XAML file, or relative to the project root if the URI starts with a /
.
Warning
The CSS file will fail to load if its build action is not set to EmbeddedResource.
Alternatively, a style sheet can be loaded and parsed with the StyleSheet
class, before being added to a ResourceDictionary
, by inlining it in a CDATA
section:
<ContentPage ...>
<ContentPage.Resources>
<StyleSheet>
<![CDATA[
^contentpage {
background-color: lightgray;
}
]]>
</StyleSheet>
</ContentPage.Resources>
...
</ContentPage>
For more information about resource dictionaries, see Resource Dictionaries.
C#
In C#, a style sheet can be loaded from a StringReader
and added to a ResourceDictionary
:
public partial class MyPage : ContentPage
{
public MyPage()
{
InitializeComponent();
using (var reader = new StringReader("^contentpage { background-color: lightgray; }"))
{
this.Resources.Add(StyleSheet.FromReader(reader));
}
}
}
The argument to the StyleSheet.FromReader
method is the TextReader
that has read the style sheet.
Selecting elements and applying properties
CSS uses selectors to determine which elements to target. Styles with matching selectors are applied consecutively, in definition order. Styles defined on a specific item are always applied last. For more information about supported selectors, see Selector Reference.
CSS uses properties to style a selected element. Each property has a set of possible values, and some properties can affect any type of element, while others apply to groups of elements. For more information about supported properties, see Property Reference.
Child stylesheets always override parent stylesheets if they set the same properties. Therefore, the following precedence rules are followed when applying styles that set the same properties:
- A style defined in the application resources will be overwritten by a style defined in the page resources, if they set the same properties.
- A style defined in page resources will be overwritten by a style defined in the control resources, if they set the same properties.
- A style defined in the application resources will be overwritten by a style defined in the control resources, if they set the same properties.
Important
CSS variables are unsupported.
Selecting elements by type
Elements in the visual tree can be selected by type with the case insensitive element
selector:
stacklayout {
margin: 20;
}
This selector identifies any StackLayout
elements on pages that consume the style sheet, and sets their margins to a uniform thickness of 20.
Note
The element
selector does not identify subclasses of the specified type.
Selecting elements by base class
Elements in the visual tree can be selected by base class with the case insensitive ^base
selector:
^contentpage {
background-color: lightgray;
}
This selector identifies any ContentPage
elements that consume the style sheet, and sets their background color to lightgray
.
Note
The ^base
selector is specific to Xamarin.Forms, and isn't part of the CSS specification.
Selecting an element by name
Individual elements in the visual tree can be selected with the case sensitive #id
selector:
#listView {
background-color: lightgray;
}
This selector identifies the element whose StyleId
property is set to listView
. However, if the StyleId
property is not set, the selector will fall back to using the x:Name
of the element. Therefore, in the following XAML example, the #listView
selector will identify the ListView
whose x:Name
attribute is set to listView
, and will set it's background color to lightgray
.
<ContentPage ...>
<ContentPage.Resources>
<StyleSheet Source="/Assets/styles.css" />
</ContentPage.Resources>
<StackLayout>
<ListView x:Name="listView" ...>
...
</ListView>
</StackLayout>
</ContentPage>
Selecting elements with a specific class attribute
Elements with a specific class attribute can be selected with the case sensitive .class
selector:
.detailPageTitle {
font-style: bold;
font-size: medium;
text-align: center;
}
.detailPageSubtitle {
text-align: center;
font-style: italic;
}
A CSS class can be assigned to a XAML element by setting the StyleClass
property of the element to the CSS class name. Therefore, in the following XAML example, the styles defined by the .detailPageTitle
class are assigned to the first Label
, while the styles defined by the .detailPageSubtitle
class are assigned to the second Label
.
<ContentPage ...>
<ContentPage.Resources>
<StyleSheet Source="/Assets/styles.css" />
</ContentPage.Resources>
<ScrollView>
<StackLayout>
<Label ... StyleClass="detailPageTitle" />
<Label ... StyleClass="detailPageSubtitle"/>
...
</StackLayout>
</ScrollView>
</ContentPage>
Selecting child elements
Child elements in the visual tree can be selected with the case insensitive element element
selector:
listview image {
height: 60;
width: 60;
}
This selector identifies any Image
elements that are children of ListView
elements, and sets their height and width to 60. Therefore, in the following XAML example, the listview image
selector will identify the Image
that's a child of the ListView
, and sets its height and width to 60.
<ContentPage ...>
<ContentPage.Resources>
<StyleSheet Source="/Assets/styles.css" />
</ContentPage.Resources>
<StackLayout>
<ListView ...>
<ListView.ItemTemplate>
<DataTemplate>
<ViewCell>
<Grid>
...
<Image ... />
...
</Grid>
</ViewCell>
</DataTemplate>
</ListView.ItemTemplate>
</ListView>
</StackLayout>
</ContentPage>
Note
The element element
selector does not require the child element to be a direct child of the parent – the child element may have a different parent. Selection occurs provided that an ancestor is the specified first element.
Selecting direct child elements
Direct child elements in the visual tree can be selected with the case insensitive element>element
selector:
stacklayout>image {
height: 200;
width: 200;
}
This selector identifies any Image
elements that are direct children of StackLayout
elements, and sets their height and width to 200. Therefore, in the following XAML example, the stacklayout>image
selector will identify the Image
that's a direct child of the StackLayout
, and sets its height and width to 200.
<ContentPage ...>
<ContentPage.Resources>
<StyleSheet Source="/Assets/styles.css" />
</ContentPage.Resources>
<ScrollView>
<StackLayout>
...
<Image ... />
...
</StackLayout>
</ScrollView>
</ContentPage>
Note
The element>element
selector requires that the child element is a direct child of the parent.
Selector reference
The following CSS selectors are supported by Xamarin.Forms:
Selector | Example | Description |
---|---|---|
.class |
.header |
Selects all elements with the StyleClass property containing 'header'. Note that this selector is case sensitive. |
#id |
#email |
Selects all elements with StyleId set to email . If StyleId is not set, fallback to x:Name . When using XAML, x:Name is preferred over StyleId . Note that this selector is case sensitive. |
* |
* |
Selects all elements. |
element |
label |
Selects all elements of type Label , but not subclasses. Note that this selector is case insensitive. |
^base |
^contentpage |
Selects all elements with ContentPage as the base class, including ContentPage itself. Note that this selector is case insensitive and isn't part of the CSS specification. |
element,element |
label,button |
Selects all Button elements and all Label elements. Note that this selector is case insensitive. |
element element |
stacklayout label |
Selects all Label elements inside a StackLayout . Note that this selector is case insensitive. |
element>element |
stacklayout>label |
Selects all Label elements with StackLayout as a direct parent. Note that this selector is case insensitive. |
element+element |
label+entry |
Selects all Entry elements directly after a Label . Note that this selector is case insensitive. |
element~element |
label~entry |
Selects all Entry elements preceded by a Label . Note that this selector is case insensitive. |
Styles with matching selectors are applied consecutively, in definition order. Styles defined on a specific item are always applied last.
Tip
Selectors can be combined without limitation, such as StackLayout>ContentView>label.email
.
The following selectors are currently unsupported:
[attribute]
@media
and@supports
:
and::
Note
Specificity, and specificity overrides are unsupported.
Property reference
The following CSS properties are supported by Xamarin.Forms (in the Values column, types are italic, while string literals are gray
):
Property | Applies to | Values | Example |
---|---|---|---|
align-content |
FlexLayout |
stretch | center | start | end | spacebetween | spacearound | spaceevenly | flex-start | flex-end | space-between | space-around | initial |
align-content: space-between; |
align-items |
FlexLayout |
stretch | center | start | end | flex-start | flex-end | initial |
align-items: flex-start; |
align-self |
VisualElement |
auto | stretch | center | start | end | flex-start | flex-end | initial |
align-self: flex-end; |
background-color |
VisualElement |
color | initial |
background-color: springgreen; |
background-image |
Page |
string | initial |
background-image: bg.png; |
border-color |
Button , Frame , ImageButton |
color | initial |
border-color: #9acd32; |
border-radius |
BoxView , Button , Frame , ImageButton |
double | initial |
border-radius: 10; |
border-width |
Button , ImageButton |
double | initial |
border-width: .5; |
color |
ActivityIndicator , BoxView , Button , CheckBox , DatePicker , Editor , Entry , Label , Picker , ProgressBar , SearchBar , Switch , TimePicker |
color | initial |
color: rgba(255, 0, 0, 0.3); |
column-gap |
Grid |
double | initial |
column-gap: 9; |
direction |
VisualElement |
ltr | rtl | inherit | initial |
direction: rtl; |
flex-direction |
FlexLayout |
column | columnreverse | row | rowreverse | row-reverse | column-reverse | initial |
flex-direction: column-reverse; |
flex-basis |
VisualElement |
float | auto | initial . In addition, a percentage in the range 0% to 100% can be specified with the % sign. |
flex-basis: 25%; |
flex-grow |
VisualElement |
float | initial |
flex-grow: 1.5; |
flex-shrink |
VisualElement |
float | initial |
flex-shrink: 1; |
flex-wrap |
VisualElement |
nowrap | wrap | reverse | wrap-reverse | initial |
flex-wrap: wrap-reverse; |
font-family |
Button , DatePicker , Editor , Entry , Label , Picker , SearchBar , TimePicker , Span |
string | initial |
font-family: Consolas; |
font-size |
Button , DatePicker , Editor , Entry , Label , Picker , SearchBar , TimePicker , Span |
double | namedsize | initial |
font-size: 12; |
font-style |
Button , DatePicker , Editor , Entry , Label , Picker , SearchBar , TimePicker , Span |
bold | italic | initial |
font-style: bold; |
height |
VisualElement |
double | initial |
min-height: 250; |
justify-content |
FlexLayout |
start | center | end | spacebetween | spacearound | spaceevenly | flex-start | flex-end | space-between | space-around | initial |
justify-content: flex-end; |
letter-spacing |
Button , DatePicker , Editor , Entry , Label , Picker , SearchBar , SearchHandler , Span , TimePicker |
double | initial |
letter-spacing: 2.5; |
line-height |
Label , Span |
double | initial |
line-height: 1.8; |
margin |
View |
thickness | initial |
margin: 6 12; |
margin-left |
View |
thickness | initial |
margin-left: 3; |
margin-top |
View |
thickness | initial |
margin-top: 2; |
margin-right |
View |
thickness | initial |
margin-right: 1; |
margin-bottom |
View |
thickness | initial |
margin-bottom: 6; |
max-lines |
Label |
int | initial |
max-lines: 2; |
min-height |
VisualElement |
double | initial |
min-height: 50; |
min-width |
VisualElement |
double | initial |
min-width: 112; |
opacity |
VisualElement |
double | initial |
opacity: .3; |
order |
VisualElement |
int | initial |
order: -1; |
padding |
Button , ImageButton , Layout , Page |
thickness | initial |
padding: 6 12 12; |
padding-left |
Button , ImageButton , Layout , Page |
double | initial |
padding-left: 3; |
padding-top |
Button , ImageButton , Layout , Page |
double | initial |
padding-top: 4; |
padding-right |
Button , ImageButton , Layout , Page |
double | initial |
padding-right: 2; |
padding-bottom |
Button , ImageButton , Layout , Page |
double | initial |
padding-bottom: 6; |
position |
FlexLayout |
relative | absolute | initial |
position: absolute; |
row-gap |
Grid |
double | initial |
row-gap: 12; |
text-align |
Entry , EntryCell , Label , SearchBar |
left | top | right | bottom | start | center | middle | end | initial . left and right should be avoided in right-to-left environments. |
text-align: right; |
text-decoration |
Label , Span |
none | underline | strikethrough | line-through | initial |
text-decoration: underline, line-through; |
text-transform |
Button ,Editor , Entry , Label , SearchBar , SearchHandler |
none | default | uppercase | lowercase | initial |
text-transform: uppercase; |
transform |
VisualElement |
none , rotate , rotateX , rotateY , scale , scaleX , scaleY , translate , translateX , translateY , initial |
transform: rotate(180), scaleX(2.5); |
transform-origin |
VisualElement |
double, double | initial |
transform-origin: 7.5, 12.5; |
vertical-align |
Label |
left | top | right | bottom | start | center | middle | end | initial |
vertical-align: bottom; |
visibility |
VisualElement |
true | visible | false | hidden | collapse | initial |
visibility: hidden; |
width |
VisualElement |
double | initial |
min-width: 320; |
Note
initial
is a valid value for all properties. It clears the value (resets to default) that was set from another style.
The following properties are currently unsupported:
all: initial
.- Layout properties (box, or grid).
- Shorthand properties, such as
font
, andborder
.
In addition, there's no inherit
value and so inheritance isn't supported. Therefore you can't, for example, set the font-size
property on a layout and expect all the Label
instances in the layout to inherit the value. The one exception is the direction
property, which has a default value of inherit
.
Targeting Span
elements has a known issue preventing spans from being the target of CSS styles by both element and name (using the #
symbol). The Span
element derives from GestureElement
, which does not have the StyleClass
property so spans do not support CSS class targeting. For more information, see Not able to apply CSS styling to Span control.
Xamarin.Forms specific properties
The following Xamarin.Forms specific CSS properties are also supported (in the Values column, types are italic, while string literals are gray
):
Property | Applies to | Values | Example |
---|---|---|---|
-xf-bar-background-color |
NavigationPage , TabbedPage |
color | initial |
-xf-bar-background-color: teal; |
-xf-bar-text-color |
NavigationPage , TabbedPage |
color | initial |
-xf-bar-text-color: gray |
-xf-horizontal-scroll-bar-visibility |
ScrollView |
default | always | never | initial |
-xf-horizontal-scroll-bar-visibility: never; |
-xf-max-length |
Entry , Editor , SearchBar |
int | initial |
-xf-max-length: 20; |
-xf-max-track-color |
Slider |
color | initial |
-xf-max-track-color: red; |
-xf-min-track-color |
Slider |
color | initial |
-xf-min-track-color: yellow; |
-xf-orientation |
ScrollView , StackLayout |
horizontal | vertical | both | initial . both is only supported on a ScrollView . |
-xf-orientation: horizontal; |
-xf-placeholder |
Entry , Editor , SearchBar |
quoted text | initial |
-xf-placeholder: Enter name; |
-xf-placeholder-color |
Entry , Editor , SearchBar |
color | initial |
-xf-placeholder-color: green; |
-xf-spacing |
StackLayout |
double | initial |
-xf-spacing: 8; |
-xf-thumb-color |
Slider , Switch |
color | initial |
-xf-thumb-color: limegreen; |
-xf-vertical-scroll-bar-visibility |
ScrollView |
default | always | never | initial |
-xf-vertical-scroll-bar-visibility: always; |
-xf-vertical-text-alignment |
Label |
start | center | end | initial |
-xf-vertical-text-alignment: end; |
-xf-visual |
VisualElement |
string | initial |
-xf-visual: material; |
Xamarin.Forms Shell specific properties
The following Xamarin.Forms Shell specific CSS properties are also supported (in the Values column, types are italic, while string literals are gray
):
Property | Applies to | Values | Example |
---|---|---|---|
-xf-flyout-background |
Shell |
color | initial |
-xf-flyout-background: red; |
-xf-shell-background |
Element |
color | initial |
-xf-shell-background: green; |
-xf-shell-disabled |
Element |
color | initial |
-xf-shell-disabled: blue; |
-xf-shell-foreground |
Element |
color | initial |
-xf-shell-foreground: yellow; |
-xf-shell-tabbar-background |
Element |
color | initial |
-xf-shell-tabbar-background: white; |
-xf-shell-tabbar-disabled |
Element |
color | initial |
-xf-shell-tabbar-disabled: black; |
-xf-shell-tabbar-foreground |
Element |
color | initial |
-xf-shell-tabbar-foreground: gray; |
-xf-shell-tabbar-title |
Element |
color | initial |
-xf-shell-tabbar-title: lightgray; |
-xf-shell-tabbar-unselected |
Element |
color | initial |
-xf-shell-tabbar-unselected: cyan; |
-xf-shell-title |
Element |
color | initial |
-xf-shell-title: teal; |
-xf-shell-unselected |
Element |
color | initial |
-xf-shell-unselected: limegreen; |
Color
The following color
values are supported:
X11
colors, which match CSS colors, UWP pre-defined colors, and Xamarin.Forms colors. Note that these color values are case insensitive.- hex colors:
#rgb
,#argb
,#rrggbb
,#aarrggbb
- rgb colors:
rgb(255,0,0)
,rgb(100%,0%,0%)
. Values are in the range 0-255, or 0%-100%. - rgba colors:
rgba(255, 0, 0, 0.8)
,rgba(100%, 0%, 0%, 0.8)
. The opacity value is in the range 0.0-1.0. - hsl colors:
hsl(120, 100%, 50%)
. The h value is in the range 0-360, while s and l are in the range 0%-100%. - hsla colors:
hsla(120, 100%, 50%, .8)
. The opacity value is in the range 0.0-1.0.
Thickness
One, two, three, or four thickness
values are supported, each separated by white space:
- A single value indicates uniform thickness.
- Two values indicate vertical then horizontal thickness.
- Three values indicate top, then horizontal (left and right), then bottom thickness.
- Four values indicate top, then right, then bottom, then left thickness.
Note
CSS thickness
values differ from XAML Thickness
values. For example, in XAML a two-value Thickness
indicates horizontal then vertical thickness, while a four-value Thickness
indicates left, then top, then right, then bottom thickness. In addition, XAML Thickness
values are comma delimited.
NamedSize
The following case insensitive namedsize
values are supported:
default
micro
small
medium
large
The exact meaning of each namedsize
value is platform-dependent and view-dependent.
Functions
Linear and radial gradients can be specified using the linear-gradient()
and radial-gradient()
CSS functions, respectively. The result of these functions should be assigned to the background
property of a control.
CSS in Xamarin.Forms with Xamarin.University
Xamarin.Forms 3.0 CSS video