A project that allows Bevy UI Node Styles to be defined with CSS strings, rather than the current inline code definitions. Mozilla's excellent cssparser & selectors crates provide the core parsing functionality.
If you like this project and would like to help make it better, please consider submitting a pull request.
To include this plugin in your project, add the following to your dependencies:
bevy_prototype_css = { git = "https://github.com/sharky-david/bevy_prototype_css" }
Alternatively clone the repo if you would like to try the included examples.
Styling/definition in CSS is supported for the following components:
bevy::ui::Style
bevy::ui::UiColor
Example:
bevy_ui_stylesheet.rs
/assets/styles/bevy_ui.css
(cargo run --example bevy_ui_stylesheet
)
The CssPlugin
allows UI styles to be defined in a .css
asset file (e.g. assets/styles/ui.css
). By loading this
asset and tagging your styled entities with a CssTag
, the stylesheet styles will be applied to your nodes for you.
Only entities with a CssTag
will be styled.
src/main.rs
:
use bevy_prototype_css::{CssPlugin, CssStylesheet, CssTag}; // Required imports
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_plugin(CssPlugin) // CSSPlugin does the hard work
.add_startup_system(setup)
.run()
}
fn setup(
mut commands: Commands,
asset_server: Res<AssetServer>,
) {
let sheet: Handle<CssStylesheet> = asset_server.load("styles/ui.css"); // Load the .css file
commands
.spawn_bundle(NodeBundle::default())
.insert(CssTag::from("#container-1.fill-width")); // Tag the entity with your id/classes
}
assets/styles/ui.css
:
#container-1 { height: 10em; color: blue; }
.fill-width { width: 100%; }
Caveat: Selector matching is currently very rudimentary. Ids and classes can be combined (e.g.
#id.class-1.class-2
), but there is currently no hierarchical matching (e.g. #parent>.child
doesn't work). There
is also no pseudo-class (e.g. :hover
), pseudo-element (e.g. ::after
), nor attribute (e.g. [attr=value]
) matching.
Example:
bevy_ui_inline.rs
(cargo run --example bevy_ui_inline
)
UI styles can be created inline in your code from a css string and the appropriate (static) context. Use CssStyle
to
define your style, then call .to_style(css_context)
to get a bevy::ui::Style
component, or .to_ui_color()
to get
a bevy::ui::UiColor
component.
CssStyle
is not a component, just a container for &str
. You could create common CssStyle
structs ahead of
time, then call .to_style
on the same CssStyle
multiple times.
src/main.rs
:
use bevy_prototype_css::{CssContext, CssStyle}; // Required imports
fn main() {
App::new()
.add_plugins(DefaultPlugins)
.add_startup_system(setup)
.run()
}
fn setup(mut commands: Commands) {
let css_context = CssContext::default(); // CssContext is required!
commands.spawn_bundle(NodeBundle {
style: CssStyle("width: 100%; height: 10em;") // Define your styles without selectors
.to_style(css_context), // Call .to_style to get your Style
color: CssStyle("color: red;").to_ui_color(), // Works for colors with .to_ui_color()
..Default::default(),
});
}
- Proper & full testing
- Hierarchical selector matching (e.g.
#parent>.child
) - Entity components as CSS tags (e.g.
Node.class { /* ... */ }
in your stylesheet) @font-face
definitions for font asset loading- Support for the following
ui::Node
component typestext::TextStyle
ui::UiImage
calc()
and other css functions- Full set of CSS spec
<length>
dimensions !important
keyword- CSS wide keywords (
initial
,inherit
,unset
)
bevy_prototype_css version |
Minimum bevy version |
---|---|
0.1 | 0.7 |
0.2 | 0.8 |
ui::Style
doc: https://docs.rs/bevy/latest/bevy/ui/struct.Style.html
- Developer reference (Mozilla): https://developer.mozilla.org/en-US/docs/Web/CSS
- Values & Units Spec: https://drafts.csswg.org/css-values/
- w3 Schools tutorials & reference: https://www.w3schools.com/css
<Bevy Type>
-> <css-property-name>
NB: Not all Bevy types have exactly the same name as their CSS properties. This is to stay consistent with web CSS style names.
Style::Display
->display
Style::Direction
->direction
Style::Size
->width
,height
Style::MinSize
->min-width
,min-height
Style::MaxSize
->max-width
,max-height
Style::Overflow
->overflow
Style::PositionType
->position
Style::Position
->top
,right
,bottom
,left
Style::FlexDirection
->flex-direction
Style::FlexWrap
->flex-wrap
Style::FlexGrow
->flex-grow
Style::FlexShrink
->flex-shrink
Style::FlexBasis
->flex-basis
Style::AspectRatio
->aspect-ratio
Style::AlignItems
->align-items
Style::AlignSelf
->align-self
Style::AlignContent
->align-content
Style::JustifyContent
->justify-content
Style::Margin
->margin
,margin-top
,margin-right
,margin-bottom
,margin-left
Style::Padding
->padding
,padding-top
,padding-right
,padding-bottom
,padding-left
Style::Border
->border-width
,border-width-top
,border-width-right
,border-width-bottom
,border-width-left
UiColor
->color
- display:
flex
|none
- direction:
ltr
|rtl
|inherit
- width:
auto
|<length>
|<percentage>
- height:
auto
|<length>
|<percentage>
- min-width:
auto
|<length>
|<percentage>
- min-height:
auto
|<length>
|<percentage>
- max-width:
auto
|<length>
|<percentage>
- max-height:
auto
|<length>
|<percentage>
- overflow:
visible
|hidden
- position:
relative
|absolute
- top:
auto
|<length>
|<percentage>
- right:
auto
|<length>
|<percentage>
- bottom:
auto
|<length>
|<percentage>
- left:
auto
|<length>
|<percentage>
- flex-direction:
row
|row-reverse
|column
|column-reverse
- flex-wrap:
nowrap
|wrap
|wrap-reverse
- flex-grow:
<non-negative-number>
- flex-shrink:
<non-negative-number>
- flex-basis:
auto
|<length>
|<percentage>
- aspect-ratio:
auto
|<ratio>
- align-items:
stretch
|center
|flex-start
|flex-end
|baseline
- align-self:
auto
|stretch
|center
|flex-start
|flex-end
|baseline
- align-content:
stretch
|center
|flex-start
|flex-end
|space-between
|space-around
- justify-content:
flex-start
|flex-end
|center
|space-between
|space-around
|space-evenly
- margin: [
auto
|<length>
|<percentage>
]{1,4} (See Shorthand below) - margin-top:
auto
|<length>
|<percentage>
- margin-right:
auto
|<length>
|<percentage>
- margin-bottom:
auto
|<length>
|<percentage>
- margin-left:
auto
|<length>
|<percentage>
- padding: [
auto
|<length>
|<percentage>
]{1,4} (See Shorthand below) - padding-top:
auto
|<length>
|<percentage>
- padding-right:
auto
|<length>
|<percentage>
- padding-bottom:
auto
|<length>
|<percentage>
- padding-left:
auto
|<length>
|<percentage>
- border-width: [
auto
|<length>
|<percentage>
]{1,4} (See Shorthand below) - border-width-top:
auto
|<length>
|<percentage>
- border-width-right:
auto
|<length>
|<percentage>
- border-width-bottom:
auto
|<length>
|<percentage>
- border-width-left:
auto
|<length>
|<percentage>
- color:
none
|transparent
|<rgb()>
|<rgba()>
|<hsl()>
|<hsla()>
|<hex-color>
|<named-color>
(See Colors below)
- Same as
<numer>
, except the value has to be>= 0
- CSS Spec
- Mozilla Web Docs
- 96 DPI is assumed. This means
1in
==96px
, regardless of your actual dpi setting. This is part of the CSS spec. - Not all dimensions in the CSS spec are accepted by this parser.
- The following dimensions are accepted:
- Absolute:
px
,cm
,mm
,Q
,in
,pc
,pt
- Font Relative:
em
,rem
,ex
,ch
- Viewport Relative:
vw
,vh
,vmin
,vmax
- Absolute:
- CSS Spec
- Mozilla Web Docs
- Possible angle units are
deg
,grad
,rad
, orturn
Allows multiple properties to be set in one declaration.
[value type
| value type
| ... ]{min
,max
}
Accepts some number of value type
s, at least min
, and at most max
times. Separate each value type
with a space.
Where a property is shorthand for the 4 sides, the order is always: -top
, -right
, -bottom
, -left
.
margin: [auto
| <length>
| <percentage>
]{1,4}
Shorthand for
margin-top
,margin-right
,margin-bottom
,margin-left
The margin
shorthand property accepts either one, two, three, or four (i.e. '{1,4}')
values. Each value can have the type of either auto
, <length>
, or <percentage>
(i.e. '[auto
| <length>
| <percentage>
]').
- If only one value is given, then all the full properties will use that same value.
- If two values are given, then the first value will be used for the
-top
and-bottom
, while the second value will be used for the-right
and-left
. - If three values are given, then the first value will be used for the
-top
, the second will be used for the-right
and-left
, and the third will be used for the-bottom
. - If all four values are given, then they will each be used for
-top
,-right
,-bottom
,-left
respectively.
The following are all valid margin
declarations:
margin: auto;
margin: 10px;
margin: 2% 1em;
margin: 20mm auto 0.3%;
margin: 1px 0.5% auto auto;
- CSS Spec
- Mozilla Web Docs
- The
none
keyword has been added to line up with Bevy'sColor::None
variantnone
is equivalent to CSStransparent
, which is also available
- The
currentcolor
CSS keyword will be ignored - The
hwb()
color function is not supported - Color definitions are quite versatile, and can be a bit complicated. The CSS docs help a lot.
- A small 'gotcha' is that all colors defined with CSS will be an instance of bevy Color::Rgba
- Format is: 'rgb([
<number>
|<percentage>
]{3} [,<number>
|<percentage>
]?)' - Both versions of this function accept the same arguments, despite the names
- The first 3 values are (in order) red, green, and blue
- If these are given as a
<number>
, they must be in the range0 ... 255
- If these are given as a
- The 4th value is for alpha, and is optional
- If a
<number>
is given, it must be in the range0.0 ... 1.0
- If a
- See also: W3 Schools
- Format is: 'hsl([
<number>
|<angle>
],<percentage>
,<percentage>
[,<number>
|<percentage>
]?)' - Both versions of this function accept the same arguments, despite the names
- The first value (
<number>
|<angle>
) is hue, representing the color/hue angle.- If no unit is given,
deg
is assumed. i.e. the number must be in the range0 ... 360
- If no unit is given,
- The second value is saturation
- The third value is lightness
- The 4th value is for alpha, and is optional
- If a
<number>
is given, it must be in the range0.0 ... 1.0
- If a
- See also: W3 Schools
- Format is:
#<red><green><blue>
(i.e.#RRGGBB
|#RGB
) - Each of
<red>
,<green>
, and<blue>
is a 2 digit hexadecimal integers (between00 ... FF
)00
is none of the respective color, whileFF
is full intensity for the respective color
- As a shorthand, each color can be specified with only 1 digit.
- In this case the digit is repeated for the color. Eg:
#fff
->#ffffff
&#abc
->#aabbcc
- If using shorthand, all 3 colors must use only 1 digit
- In this case the digit is repeated for the color. Eg:
- Case-insensitive
- CSS Spec
- W3 Schools HEX Color Helper
- These are the CSS named colors, and may not be equivalent to the bevy color consts
- Use the links below for a full list of the available named colors
- In particular, I recommend reading the 'gotcha' note in the Mozilla Web Docs
- Keep in mind that some colors may look different on different monitors/color profiles
- CSS Spec
- Mozilla Web Docs
This crate is dual licensed under the MIT license or the Apache 2.0 license.
Copies of these licenses are available in the docs
folder.