Open 0kku opened 3 years ago
While this would technically be somewhat trivial to implement, after some deliberation, I'm not convinced it's necessarily a good idea. Encouraging globally declared rules is a little orthogonal to the design of the rest of the library, and kind of defeats the whole point of styles being scoped by the components.
Since this issue was first raised, support for static styles has been added, which allow you to reuse styles:
class Foo extends Component {
static styles = [
myReusedStyles,
css`
/* styles specific to the component */
`,
];
}
It may be less convenient to have to explicitly opt into using the styles you intend to be global in every component, but explicitness tends to make debugging things easier and tends to be less bug prone.
Whilst I didn't know we could do something like [reusableStyles, css...]
Here's my initial thoughts:
Why would people want to use global styles?
They have a global.css
stylesheet, that may give a common style to all input
element, buttons etc. This isn't really achievable with scoped css because you'd have to rewrite the same CSS (stay with me here)
Pulling in libraries, eg <link rel="/public/css/boostrap.min.css">
or pulling it through a CDN
I personally think these are very valid usecases. As of now, they cannot be done. (stay with me...). Which leads me on to...
How can we address those?
// ./public/components/styles.ts
// Instead of your view linking to the css file, that css file is instead converted to a JS exported variabled
import { html } from ...
export const globalStyles = css`
input { color: red }
`
// ./public/components/Chat.ts
import { globalStyles } from ...
class Chat extends Component {
static styles = [
globalStyles,
css`
.some-scoped-elem { ... }
`
]
}
Now I guess this bring a slight problem: every component will have to add this same line. Now is that a bad thing? Maybe it's subjective. So maybe it's down to @0kku as to whether they implement some logic to apply 'global styles' to components, which from what I understand, is considered bad practice: the whole idea of the shadow dom is only scoped css applies.
I'm guessing there must some some projects (company software, pet projects etc) that use a shadow dom and have a common UI/styling, where styles are shared across components. I'm just curious how they handle it?
Importing CSS from URLs is not yet supported directly by the static styles
property. There are ways you could convert a URL to a style sheet object, but none of the ways are particularly ergonomic. Import assertions may in the future help with statically importing CSS files, but I agree that there's going to have to be some stop-gap solution while we wait for the aforementioned feature to stabilize.
That being said, the code style I originally wanted to encourage was a pattern where you write components that come with styles and import them where they're being used. That way, styles are not decoupled from the structure of the component, and the component is always accompanied by the intended styles. So, for example, if you wanted to have a special button that's styled in a particular way, you'd create a StyledButton
and import where it's being used:
export class StyledButton extends Component {
static captureProps = true;
forwardProps = new Ref;
static styles = css`
button {
background: green;
}
`;
template = html`
<button destiny:ref=${this.forwardProps}>
<slot/>
</button>
`
}
and then use it like this:
import { StyledButton } from "./StyledButton.js";
html`
<!-- Other content -->
<${StyledButton}>Click me</${StyledButton}>
`;
However, there are a couple of problems with this:
I'm guessing there must some some projects (company software, pet projects etc) that use a shadow dom and have a common UI/styling, where styles are shared across components. I'm just curious how they handle it?
I have to admit that I'm not sure what the generally agreed-on way to do this is, but I would guess that the most common pattern is to include a <link rel="stylesheet" href="global-styles.css" />
tag in the component's template. This approach will work with Destiny as well, should you want to use it.
@0kku Wow that is something i've done in other porjects, but completely overlooked regarding this topic for Destiny...
I retract my original opinions and would 100% agree with you, on the fact that buttons, lists etc (any common element) should be a component and used as a 'sub component'.
To be honest, i'd argue it can be ergonomic - after all be it react or destiny, we use components right? You have a 'custom slider component'? or a button? or a form? Thats a component that can accept props (if needed).
Example:
GET /user/new
will display a UsersNew
component. That component will use FormComponent
as a 'child'. You pass in a click ahndler for the submit button. That form component will consist of using input components too
I have to admit that I'm not sure what the generally agreed-on way to do this is, but I would guess that the most common pattern is to include a tag in the component's template. This approach will work with Destiny as well, should you want to use it.
I guess that is a way, but it doesn't feel right, you know? Seems like it does go against the premise of scoped components?
Regarding general styles eg on the body
, or helper classes eg .flex
or .text-align-c
, they could be a reusableStyles
variable
The only thing i'm left thinking about is 'external' styles, again going back to bootstrap (just an example, could be tailwind, literally anything), whether it's in a lib
dir, or you're using a cdn to serve it. And from what you've said, that seems near impossible or not a very ergonomic approach
Maybe this is just a matter discussing how an application would go about using external/third party stylesheets?
After a long convesation with @0kku, re've discovered that using global stylesheets is possible:
abstract class BaseComponent extends Component {
protected html (input: TemplateResult) {
return xml`
<link rel="stylesheet" href="styles.css" />
${input}
`
}
}
class CustomButton extends BaseComponent {
override template = this.html(xml`<button>hello:)</button><p>hi as well</p>`)
}
class AppRoot extends BaseComponent {
override template = this.html(xml`<p>hello</p><${CustomButton} />`)
}
register(AppRoot)
I suggest the above and the notes from https://github.com/0kku/destiny/issues/15#issuecomment-864301877 (eg styles = [css'${globalStyles}, css'${thisComponentStyles}']
)
@0kku I'm happy to write work on a write up for this and you could add it to the wiki? afaik outsider contributors can't edit the wiki
A way of defining CSS that pierces shadow roots of components. I'm thinking of injecting a common stylesheet during component instantiation.