dioxus-class, dioxus-tailwindcss, dioxus-daisyui
Writing dioxus class with compile time checked constan
I've just published a few crates lately, they are pretty simple, mostly just a bunch of constants for CSS class names, then I can write dioxus apps with compile time checked class attributes.
Why I created these crates?
I want to create some GUI applications with Rust, based on my limited experience in frontend development, the Elm approach gave me the best developing experience, so logically I want something in similar design in Rust. After some research, seems Dioxus is the best option at the moment, so I started making some prototype.
The prototype is a very basic web app to show all emojis in a grid, and you can search over them by fuzzy matching their aliases, there is a Live Demo of the current version, you may have a look.
It's generally smooth, though the documentation is pretty basic (at least comparing to Bevy, which is quite nice), and I met a building issue with dioxus-cli, but that's not the topic of this post.
Since Dioxus is mostly targeting to HTML (there are some experimental other options, but none of them are mature enough), to have a good looking result, CSS is still needed, the Dioxus doc site is using tailwindcss, so I looked into it a bit, which is using a so called Utility-First way, which is pretty neat IMO, so I want to use it in my prototype too.
But I don't want to spend too much time to become an export with CSS, so did some quick look about pre designed components using tailwindcss, I do like daisyui, so want to use that as well.
So far so good, I got a grid of cards showing emoji name and icons rather easily, of course need to learn Dioxus along the way (due to the lack of documents, need to read the codes a bit, which is very high quality, but quite easy to get lost for beginner), most of them are well designed and nice to use, my main complain is the way to define classes.
cx.render(rsx!{
div {
class: "card card-compact w-64 h-64 bg-base-300 shadow-xl text-center hover:bg-base-200 hover:scale-105",
div {
class: "text-8xl py-10",
cx.props.value
},
div {
class: "card-body text-center items-center hover:scale_105",
div {
class: "card-title text-sm text-base-content",
cx.props.alias
}
}
}
})
This is a code snippet for showing one emoji card, it seems straight-forward, but all the string constants make me not happy, they are fragile to typo, auto completion can't be provide in Rust, it's especially hard to reuse or re-organize these values for bigger projects. The bottom line is that I don't want to spend my time writing these strings during development.
I this some one else must've created something for this situation, and did found one.
But it's not really my taste, at least I don't want to write my code like this, too much noise here.
use gen::{C, M};
let class = [[M.lg, C.siz.w_6].join(":").as_str(), C.typ.text_lg].join(" ");
// There are also different ways, but not like them either.
Also I realize that to make progress in my prototype, I keep reading both documents of tailwindcss and daisyui all the time (both pretty neat), so I decided to spend some time to create some simple crates so I can use them in a cleaner way.
Current status
After a few days working on the prototype, I've got the class crates in a useable state.
At least for me, it's a huge step forward, all the constants are ready to use, the auto completion is natively supported as any other Rust const, it's very simple to follow documents in daisyui to experiment in my prototype writing.
This is the source code of a component using dioxus_daisyui: emoji_card.rs
use dioxus::prelude::*;
use dioxus_daisyui::prelude::*;
#[derive(Props, Clone, PartialEq, Eq, PartialOrd, Ord, Hash)]
pub struct Props<'a> {
pub alias: &'a str,
pub value: &'a str,
}
pub fn view<'a>(cx: Scope<'a, Props<'a>>) -> Element {
cx.render(rsx!{
div {
class: class!(card card_compact w_64 h_64 bg_base_300 shadow_xl text_center hover(bg_base_200) hover(scale_105)),
div {
class: class!(text_8xl py_10),
cx.props.value
},
div {
class: class!(card_body text_center items_center),
div {
class: class!(card_title text_sm text_base_content),
cx.props.alias
}
}
}
})
}
It's very easy to define these constants too, here is the lines to define daisyui alert in display.rs
// https://daisyui.com/components/alert/
constant!(alert);
// Modifier
constant!(alert success);
constant!(alert warning);
constant!(alert info);
constant!(alert error);
pub const alert: &'static str = "alert";
pub const alert_success: &'static str = "alert-success";
pub const alert_warning: &'static str = "alert-warning";
pub const alert_info: &'static str = "alert-info";
pub const alert_error: &'static str = "alert-error";
(Did manually wrote hundreds of these lines before spending the time to learn more about macro rules, to make this constant! working properly, which probably will be in a separate post later).
Build process
To use tailwindcss, either you include a big CDN version with all definition (not recommended in production) or setup a build process to only include the parts been used.
This is a bit tricky for class reuse, and even harder for the dioxus approach, did try a few different ways, the current best way I figured out is still a bit tricky, but acceptable to me.
I create a separate project that depends on the demo one, then use dioxus-ssr to generate html version of pages in project, then calling tailwindcss to build final css file, details in build project in demo
This is still not ideal, but at least it works, and after running both dioxus serve
(in main project) and cargo watch --watch ../src
(in build project), with the hot-reloading support in Dioxus, it's a not too bad workflow
Future improvement
I do want to have a better way for the build process, so may keep thinking on this.
In the meantime, will try to do more works in the demo, or create some new demos, along the way, may find some new features in these class related crates.
Or may try using other component set projects, then maybe will create new crates.
Anyway, I am pretty happy with the current version, if you also dislike writing string constants in these class attributes, you may find these crates useful.