Closed dakom closed 4 years ago
Also - not quite clear on what the class!
macro is or how it works. Some explanation would be great!
Yeah of course, you just import them the same way that you import them in HTML:
html!("link", {
.attribute("rel", "stylesheet")
.attribute("href", "/path/to/foo.css")
})
How about doing that, but also being able to import common files (like "theme-dark.css", "base.css", etc.) - without having it add bloat at each additional import?
I'm not sure what you mean, but you can use @import url("base.css")
within your .css
files to import other .css
files.
Also - not quite clear on what the class! macro is or how it works. Some explanation would be great!
The class!
macro creates a CSS stylesheet, adds a CSS rule into it, and then adds CSS styles to that rule. When you do this...
lazy_static! {
static ref FOO: String = class! {
.style("background-color", "red")
.style("width", "500px")
.style("height", "500px")
};
}
...that is exactly the same as doing this in HTML:
<style>
.__class_1__ {
background-color: red;
width: 500px;
height: 500px;
}
</style>
The FOO
static contains the string "__class_1__"
, so when you use .class(&*FOO)
in dominator, that just adds the __class_1__
class to the DOM node.
So it's just a simple way of injecting CSS stylesheets into the page (with a unique classname), and then adding that classname to DOM nodes.
If you're curious how it actually does this:
It uses the sheet
property to access the CSSStyleSheet
for the <style>
element.
It creates a unique classname, and then inserts a CSS rule. It does this by using the cssRules
and insertRule
APIs.
It now has a CSSStyleDeclaration (which is the same as the .style
property on DOM nodes, except it modifies the <style>
).
So it can now set CSS properties on that CSSStyleDeclaration (in the same way that it sets CSS properties on the .style
of DOM nodes).
Basically, if I translated dominator's code to JS, it would look like this:
let counter = 0;
function makeClassId() {
++counter;
return "__class_" + counter + "__";
}
function makeStylesheet() {
const e = document.createElement("style");
e.type = "text/css";
document.head.appendChild(e);
return e.sheet;
}
function makeStyleRule(sheet, selector) {
const rules = sheet.cssRules;
const length = rules.length;
sheet.insertRule(selector + "{}", length);
return rules[length];
}
function makeClass(f) {
const className = makeClassId();
const sheet = makeStylesheet();
const rule = makeStyleRule(sheet, "." + className);
f(rule.style);
return className;
}
const FOO = makeClass((style) => {
style.setProperty("background-color", "red");
style.setProperty("width", "500px");
style.setProperty("height", "500px");
});
document.body.className = FOO;
Ahh... thanks... seems it answers the encapsulation question too, since one can either use the link
approach within the shadowDOM, or use the dynamic class name approach anywhere.
From my perspective - feel free to close!
Once constructable stylesheets are available, it will become a lot easier to create and inject stylesheets into the DOM (including within the shadow DOM), but for now creating <link>
and <style>
within the shadow DOM is your best option.
Any plans to do constructable stylesheets in the near future? They're now available in Chrome and in Firefox
They're still not available in Safari, they only have 61% availability.
Is the ability to use it with a polyfill not enough?
There aren't any correct polyfills, because this feature can't actually be polyfilled. For example, this polyfill has various limitations and issues. Do you need this feature?
I do need it. In our current setup we use Lit to create custom elements and Dominator to render those elements from Rust. We need Lit since Dominator doesn't have components where you can scope styles. I think that constructable stylesheets combined with .shadow_root!
could solve that need for our codebase and help us get rid of Lit and custom elements.
This might be a long shot, how about adding and actual style tag for browsers that don't support constructable stylesheets? This is the way Lit does it and it works quite well.
Oh that's a different issue, I already had plans to support a dynamic class!
which can be inserted/removed from the DOM (including shadow roots). That can be easily done without adoptedStyleSheets
.
You can currently do that right now by the way:
html!("div", {
.shadow_root!(ShadowRootMode::Closed => {
.child(html!("style", {
.text("...")
}))
.child(...)
})
})
This is great! But I still need adoptedStyleSheets
for browsers that do support it, any way to do that?
:+1: for dynamic classes
Why do you need that?
Since you have full access to the DOM, there's nothing stopping you from doing that:
#[wasm_bindgen(inline_js = "
export function set_stylesheets(node, sheets) {
node.adoptedStyleSheets = sheets;
}
export function new_stylesheet() {
return new CSSStyleSheet();
}
")]
extern "C" {
fn set_stylesheets(node: &Node, sheets: &Array);
fn new_stylesheet() -> CssStyleSheet;
}
html!("div", {
.shadow_root!(ShadowRootMode::Closed => {
.before_inserted(|node| {
let style = new_stylesheet();
// Set styles on the stylesheet...
set_stylesheets(&node, &[style].into_iter().collect::<Array>());
})
.child(...)
})
})
I need that so that I can have the same component multiple times on the same page without ending up with lots of the same stylesheets.
Haven't thought about doing it manually, sounds like a great option! Thanks!
It will be a bit tricky to figure out the best API for this, which is why I haven't implemented it earlier... I'm thinking something like this seems reasonable:
static CLASS: Lazy<ClassBuilder> = Lazy::new(|| class_builder! {
.style("width", "100px")
.style("margin", "5px")
.style("background-color", "red")
});
html!("div", {
.shadow_root!(web_sys::ShadowRootMode::Closed => {
.stylesheets([
CLASS,
])
.child(html!("div", {
.class(&*CLASS)
}))
})
})
This should be future-compatible with constructable stylesheets.
Any advantage of using .style
rather then just accepting a CSS string? It's not like the CSS in .style
is strongly typed
Yes, it is consistent with DomBuilder, so you can easily copy-paste between them. And it also supports .style_signal
, .style_important
, and .style_unchecked
.
It also makes it very easy to use dynamically computed values, for example:
Also, .style
is checked at runtime to verify that it's correct. That wouldn't be possible if it was a single giant string.
dominator consistently uses the same syntax for class!
, html!
, and stylesheet!
, it would be unusual to change the syntax just for the Shadow DOM.
About consistency: what is the general advantage of using .style
in class!
/html!
/stylesheet!
? Why not use strings everywhere? Is it just for .style_signal
?
The same reasons I said above.
This has come up in a couple other issues, like https://github.com/Pauan/rust-dominator/issues/35, but I think it deserves its kindof its own issue too...
Is there a way to import .css files?
How about importing them so that they are encapsulated within a particular component/tree? (shadowDOM or not)?
How about doing that, but also being able to import common files (like "theme-dark.css", "base.css", etc.) - without having it add bloat at each additional import?