Supported CSS attributes
This is a list of CSS attributes that are currently implemented. They work inthe same way as on a regular web page, except if noted otherwise:
border-radius
background-color
color
(also as an non-standard alias:font-color
)letter-spacing
border
,border-top
,border-bottom
,border-left
,border-right
padding
,padding-top
,padding-bottom
,padding-left
,padding-right
margin
,margin-top
,margin-bottom
,margin-left
,margin-right
background
[see #1]font-size
font-family
box-shadow
,box-shadow-top
,box-shadow-bottom
,box-shadow-left
,box-shadow-right
[see #2]line-height
position
: [static
|relative
|absolute
]top
,left
,right
,bottom
(only affects relative and absolute positioned items)width
,min-width
,max-width
height
,min-height
,max-height
overflow
,overflow-x
,overflow-y
text-align
[see #3]flex-direction
flex-grow
flex-shrink
(parses, but currently has no effect)flex-wrap
(parses, but currently has no effect)justify-content
(also as an non-standard alias:align-main-axis
)align-items
(also as an non-standard alias:align-cross-axis
)
Supported pseudo selectors
:first
:nth-child(x)
:last
:hover
[WIP]:active
[WIP]:focus
[WIP]You can limit the inheritance of properties either to direct children only (using>
) or to all children(usingdiv#my_div.class
has a different effect thandiv #my_div .class
ordiv > #my_div > .class
.
Notes:
image()
takes an ID instead of a URL. Images and fonts are external resourcesthat have to be cached. Useapp.add_image("id", my_image_data)
orapp_state.add_image()
, then you can use the"id"
in the CSS to selectyour image.If an image is not present on a displayed div (i.e. you added it to the CSS,but forgot to add the image), the following happens:- In debug mode, the app crashes with a descriptive error message
- In release mode, the app doesn't display the image and logs the error
box-shadow-xxx
are non-standard extensions. They are used to clip a box-shadow toonly one border of a rectangle. The rule is:- Ifbox-shadow
is specified, a regular box shadow is drawn- Ifbox-shadow-top
andbox-shadow-bottom
are specified, the shadow appears on thetop and bottom of the rectangle. Same with a pair ofbox-shadow-left
/box-shadow-right
.- It isn't possible to display a box shadow on 3 sides, only on one or twoopposite borders.- If all 4 borders are specified, the value forbox-shadow-top
is taken.- Justified text is not (yet) supported
- The pseudo selectors marked as [WIP] currently parse, but have no effect.
Creating CSS stylesheets
When you create a window, you have to give it a certain CSS stylesheet.This stylesheet cannot be modified during runtime, which is an importantlimitation. You can, however, create "dynamic properties", which can be dynamicallyupdated.
Azul has four modes on how the CSS is created:
TODO: The following APIs are outdated since #70 (refactoring CSS parsing into external crate)!
Css::new_from_str(&str)
- parses and creates a stylesheet directlyCss::native()
- creates the styles from the default, OS-native stylesCss::override_native(&str)
- same asCss::new_from_str
, but applies the user-definedstyles on top of the OS-native styles, i.e. it "overrides" the OS-native styles with user-defined ones.If you use a stylesheet that is saved to a separate file, Azul provides two extramodes, which are, however, only available in debug mode (with#[cfg(debug_assertions)]
).Css::hot_reload(FilePath)
- hot-reloads a stylesheet from a file every 500ms, good for RAD.Css::hot_reload_override_native(FilePath)
- same ashot_reload
, but applies the user-definedstyles on top of the OS-native styles, i.e. it "overrides" the OS-native styles with user-defined ones.If there are errors during parsing, thehot_reload
function will print the error and use thelast stylesheet that could be parsed correctly.
Protected class names
In general, you should avoid defining any CSS classes in your stylesheet that startwith __azul-native-
if you use Css::native()
, override_native
or hot_reload_override_native
.Otherwise, you will override built-in native styles, unless that's what you're going for,it's better to not name your classes this way.
Static and dynamic CSS Ids
Azul knows about two types of CSS key-value pairs: static and dynamic. Becausethe CSS is only parsed once (at the start of the application), you cannot modify itduring runtime. However, you can specify IDs for certain properties, in order tochange the style of the application during runtime (for example to change the colorof a button on a On::Hover
or On::MouseDown
event). This API is also used foranimations (TODO) and :hover
, :focus
pseudo-selectors.
A static CSS property looks like this:
- .my_class {
- width: 500px;
- }
… while a dynamic property looks like this:
- .my_class {
- width: [[ my_property_id | 500px ]];
- }
The difference is that you can use the ID (in this case: "my_property_id"
tochange the style dynamically from Rust):
- use azul::prelude::{CssProperty, LayoutWidth};
- impl Layout for DataModel {
- fn layout(&self, _info: LayoutInfo<Self>) -> Dom<Self> {
- Dom::new(NodeType::Div)
- .with_class("my_class")
- .with_css_override("my_property_id", CssProperty::Width(LayoutWidth::px(700.0)))
- }
- }
This will "override" the CSS property to 700 px on the specific div. If the .with_css_override
isn't present, the width would be 500px wide (every DynamicCssProperty
need a "default" case).If you want to specify the default width to be automatically inferred from how much space thereis left in the parent node, you can use auto
like this:
- .my_class {
- width: [[ my_property_id | auto ]];
- }
For example, this is useful if you want to have panels that expand an contract on clicking a button orif you want to drag content (so you need to overwrite `margin-top / margin-left to actually move the div)in the layout.
Notice that we don't have to un-set the width again. This is because dynamicCSS properties only stay active for one frame, and need to be re-applied on eachframe - this is a concious design choice to not make the visual style depend on asequence of actions that have to be executed and thereby make the style moreunit-testing friendly.
Also notice: If the set_dynamic_property
gets a different type for your ID (let's say youtyped height
instead of width
, it will not override the target value, but print anerror (if logging is enabled)).
Layout system
The layout model follows closely to the CSS flexbox model (although many properties aren't implemented yet):
- align the content across the main axis with
flex-direction
- If the width and height is not constrained, the rectangle will take up as much widthand height as possible, by default the width / height of the parent
- Nest the layout to get more intricate and complicated layouts
- When a rectangle is marked as
position: absolute
, it will search up the DOM tree for the nearestposition: relative
orposition: absolute
rectangle and use thetop, left, right, bottom
propertiesto position itself relative to that rectangle - Z-indexing is currently only determined by the stacking order, there is no
z-index
attribute yet inline
blocks are non-existent. The reason for this is that if you need inline blocks, you'll likelyneed a bit more complicated layout. For this purpose, you can use absolute and relative positioning if youwant to lay rectangles behind the text and use the text metrics themselves to calculate the offsets.For regular inline-block content, just useflex-direction
with a wrapper div.
Remarks
- All measurements respect HiDPI screens and azul is fully HiDPI aware. Em valuesare measured as
1em = 16px
- CSS key-value pairs are parsed from top to bottom, and get overwritten inthat order, for example:
- #my_div {
- background: image("Cat01");
- background: linear-gradient("105deg, red, blue");
- }
… will draw a linear gradient, not the image, since the linear-gradient
valueoverwrote the image
rule.
- Attributes in CSS paths are not (and aren't planned to be) supported,since this is a fundamental difference between how HTML and the
layout()
function work. auto
as a value is only valid inside of dynamic CSS attributes, currentlyvalues such asauto
andinherit
do not work as general values.Animations are planned to be implemented with@keyframes
, but this is not yet supported yet.You can create preliminary "animations" by using the dynamic CSS properties, but be aware thatevery repaint with dynamic CSS Ids requires a full re-layout, which can be performance intensive.