Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Allow custom CSS file. #101

Closed
MicahZoltu opened this issue Feb 25, 2016 · 22 comments
Closed

Allow custom CSS file. #101

MicahZoltu opened this issue Feb 25, 2016 · 22 comments
Assignees
Labels

Comments

@MicahZoltu
Copy link

@MicahZoltu MicahZoltu commented Feb 25, 2016

https://github.com/aurelia/dialog/blob/master/src/ai-dialog.html#L2

It is great having some basic CSS working out of the box with the dialog. However, in order to use the dialog in any real-world application it needs to be themed to match the enclosing website. Currently, the CSS is baked in with no apparent way to change it to something else. It would be nice if a user could choose what CSS the dialog should use either at plugin setup time or at dialog open time by providing either a path to a CSS file or a CSS file that sits next to the dialog HTML with the same name.

I think the ideal order of preference would be:

  1. path to CSS supplied by user
  2. CSS sitting next to view and view-model with the same name but .css extension.
  3. CSS baked into the dialog repo (current behavior).
@EisenbergEffect

This comment has been minimized.

Copy link
Member

@EisenbergEffect EisenbergEffect commented Feb 25, 2016

I'm wondering if this is a more general feature we should investigate. Basically, the ability to override internal css for a component.

I can think of two scenarios:

  • Override CSS across the entire application
  • Override CSS on a single instance (or a select set of instances)
@EisenbergEffect EisenbergEffect self-assigned this Feb 25, 2016
@EisenbergEffect

This comment has been minimized.

Copy link
Member

@EisenbergEffect EisenbergEffect commented Feb 25, 2016

Let's brainstorm. So, what if components with overridable css did something like this

my-component.html

<template>
  <require from-"./my-component.css" can-override></require>
  ..
</template>

And then what if in app startup, you could do something like this:

aurelia.use.override('my-plugin/my-component', 'my-component.css', 'custom.css');

Or on a case-by-case basis by applying an override.

<template>
  <require from-"./custom.css" as="someName"></require>
  <my-component override-with="someName"></my-component>
</template>

Just thinking out loud...

@PWKad

This comment has been minimized.

Copy link
Contributor

@PWKad PWKad commented Feb 25, 2016

You could also do this by simply overriding ai-dialog.html with your own implementation -

my-dialog.html

<template>
  <require from="./some-other.css"></require>
  <content select="ai-dialog-header"></content>
  <content select="ai-dialog-body"></content>
  <content select="ai-dialog-footer"></content>
</template>

usage -

<template>
  <my-dialog>
    <ai-dialog-body>
      <h2>Edit first name</h2>
      <input value.bind="person.firstName" />
    </ai-dialog-body>

    <ai-dialog-footer>
      <button click.trigger="controller.cancel()">Cancel</button>
      <button click.trigger="controller.ok(person)">Ok</button>
    </ai-dialog-footer>
  </my-dialog>
</template>

This still uses all of the ai-dialog elements it just uses a different container for them so you can customize it.

@MicahZoltu

This comment has been minimized.

Copy link
Author

@MicahZoltu MicahZoltu commented Feb 25, 2016

I didn't know you could do that @PWKad, I'll try to get a PR to the readme with those details later when I get this working.

It's that a good general solution for components? Would it work and be reasonable for components with multiple pieces or more code associated with them?

I would rather be able to only override the CSS, but perhaps the engineering effort required to add that isn't worth it when the user can do what @PWKad suggested.

@PWKad

This comment has been minimized.

Copy link
Contributor

@PWKad PWKad commented Feb 25, 2016

Yeah sorry last night when we were discussing I mentioned you could override all templates but I didn't think of the most simple way to solve your issue of just that one template.

The only reason I'm timid about adding in too much configuration is that all of the questions so far have been for different pieces and I'd hate to take such a simple yet effective usage of dialogs and add in too many options for over-configuration when it works so well with rolling your own modal implementations, if that makes sense. Thoughts @EisenbergEffect ?

@EisenbergEffect

This comment has been minimized.

Copy link
Member

@EisenbergEffect EisenbergEffect commented Feb 25, 2016

I agree. I think it's pretty easy in this case to create a simple modal implementation and specify the desired styles. It did just remind me of something that might be a more general need for other types of components in the future...so I was just tossing out ideas trying to get my brain thinking about how things could work.

@MicahZoltu

This comment has been minimized.

Copy link
Author

@MicahZoltu MicahZoltu commented Feb 26, 2016

I tried creating the file you suggested (my-dialog.html) and replacing ai-dialog in my template with my-dialog but it doesn't appear to be working. Looking at my web server, I don't see my-dialog.html being requested, nor the CSS I specified. The results are that I get no styling at all, not even the default styling.

@MicahZoltu

This comment has been minimized.

Copy link
Author

@MicahZoltu MicahZoltu commented Feb 26, 2016

I added .globalResources with the path to my-dialog (including .html suffix) and now my HTML and CSS files are being fetched but I still don't see any styling being applied. I am currently using the CSS that is shipped so my expectation is that if this was working I would see a dialog as though I used ai-dialog.

@MicahZoltu

This comment has been minimized.

Copy link
Author

@MicahZoltu MicahZoltu commented Feb 26, 2016

I spent a while trying to get this working but I am once again not convinced the solution proposed will actually work. In particular, the DialogRenderer has this:

let modalOverlay = document.createElement('ai-dialog-overlay');
let modalContainer = document.createElement('ai-dialog-container');

This is going to use ai-dialog-overlay and ai-dialog-container. These will be styled with either the CSS I have supplied or the CSS supplied by aurelia-dialog in an undefined way (at least, I can't figure out how which styles will be used). This means that at best, I could re-style the ai-dialog and its components but not the container or overlay (which contain some important styling information).

As far as the internal bits of the dialog, I found that I can just make my dialog template not reference ai-dialog and manually style everything inside. This gets me part of the way to the goal, but I would still like to be able to style the rest (container and overlay).

@MicahZoltu

This comment has been minimized.

Copy link
Author

@MicahZoltu MicahZoltu commented Feb 26, 2016

Final comment for future readers. I got it working (for now) by making my template look like the following with a reference to a CSS file of my own that has all of the same things as the baked in CSS. I am not certain how reliable this is because I believe it relies on the my CSS being injected into the page further down than the baked in CSS. I also don't know if this behaves the same in all browsers (only tested Chrome). If this order of injection is guaranteed by Aurelia templating system and all browsers preference last style then this should work going forward. If not, then I'm not sure what the best solution is.

I still stand by my initial request for being able to override the CSS. Requiring me to copy the CSS from the plugin source code is a poor user experience IMO. I would rather the documentation for this plugin list the different elements that are used and allows users to fully style them as they wish without needing to be sure that they override every style that the plugin provides. Currently there are styles for overlay, container, dialog, header, body, footer and buttons and at least the header and footer have code-behind so just replacing them with <div> tags will result in a behavior change and as mentioned previously, the overlay and container can't be swapped out for different tags.

<template>
    <require from="./my-modal.css"></require>
    <ai-dialog>
        <ai-dialog-body>
            ...
        </ai-dialog-body>
    </ai-dialog>
</template>

(the contents of https://github.com/aurelia/dialog/blob/master/styles/dialog.css)
NOTE: If you remove any styles from this CSS they will be forced back to the baked in style. If you don't want a style anymore, set it to inherit or whatever value you want rather than deleting it.

ai-dialog-overlay {
  bottom: 0;
  left: 0;
  position: fixed;
  top: 0;
  right: 0;
  opacity: 0;
}
ai-dialog-overlay.active {
  opacity: 1;
}
ai-dialog-container {
  display: block;
  position: fixed;
  transition: opacity .2s linear;
  top: 0;
  right: 0;
  bottom: 0;
  left: 0;
  opacity: 0;
  overflow-x: hidden;
  overflow-y: auto;
  pointer-events: none;
  -webkit-overflow-scrolling: touch;
  outline: 0;
}
ai-dialog-container.active {
  opacity: 1;
}
ai-dialog {
  position: relative;
  display: block;
  min-width: 300px;
  box-shadow: 0 5px 15px rgba(0, 0, 0, 0.5);
  border: 1px solid rgba(0, 0, 0, 0.2);
  border-radius: 5px;
  padding: 0;
  width: -moz-fit-content;
  width: -webkit-fit-content;
  width: fit-content;
  height: -moz-fit-content;
  height: -webkit-fit-content;
  height: fit-content;
  margin: auto;
  border-image-source: initial;
  border-image-slice: initial;
  border-image-width: initial;
  border-image-outset: initial;
  border-image-repeat: initial;
  background: white;
  pointer-events: auto;
}
ai-dialog > ai-dialog-header {
  display: block;
  padding: 16px;
  border-bottom: 1px solid #e5e5e5;
}
ai-dialog > ai-dialog-header > button {
  float: right;
  border: none;
  background: none;
  font-size: 22px;
  position: relative;
  top: -4px;
  margin: 0;
  padding: 0;
  cursor: pointer;
}
ai-dialog > ai-dialog-body {
  display: block;
  padding: 16px;
}
ai-dialog > ai-dialog-footer {
  display: block;
  padding: 6px;
  border-top: 1px solid #e5e5e5;
  text-align: right;
}
ai-dialog > ai-dialog-footer button {
  color: #333;
  background-color: #fff;
  padding: 6px 12px;
  font-size: 14px;
  text-align: center;
  white-space: nowrap;
  vertical-align: middle;
  -ms-touch-action: manipulation;
  touch-action: manipulation;
  cursor: pointer;
  background-image: none;
  border: 1px solid #ccc;
  border-radius: 4px;
  margin: 5px 0 5px 5px;
}
ai-dialog > ai-dialog-footer button:disabled {
  cursor: default;
  opacity: 0.45;
}
ai-dialog > ai-dialog-footer button:hover:enabled {
  color: #333;
  background-color: #e6e6e6;
  border-color: #adadad;
}
.ai-dialog-open {
  overflow: hidden;
}
@npelletm

This comment has been minimized.

Copy link

@npelletm npelletm commented Mar 1, 2016

At this day, I used @Zoltu's technique but being able to override the CSS will be a good feature.

@jedd-ahyoung

This comment has been minimized.

Copy link
Member

@jedd-ahyoung jedd-ahyoung commented Mar 17, 2016

A simpler way to provide this functionality would be to provide a class.bind on the enclosing custom element. This would set a class on the inner element (div, etc) that could then be styled accordingly. It's the same thing as the override-css property, but it's native and simpler. I've used this in custom elements that I've created and it's been successful.

Essentially, if we have a custom element template like this:

<template>
    <div id="wrapping-element">
        <content select="custom-element-header"></content>
        <content select="custom-element-body"></content>
        <content select="custom-element-footer"></content>
    </div>
</div>

...then all we need to do is do this:

<template>
    <div id="wrapping-element" class.bind="class">
        <content select="custom-element-header"></content>
        <content select="custom-element-body"></content>
        <content select="custom-element-footer"></content>
    </div>
</div>

...and define a "class" bindable on the custom element itself. Following this pattern down the hierarchy (for custom-element-header, etc) means that a developer can reference his or her own class without having to touch the innards of the element. Further, we can specify a default value for the class binding, so that we can still provide default CSS styles. All of this can be done without an extra override-css property.

@EisenbergEffect I'd advocate that this is good practice for any custom element. Do you see any pitfalls to this approach?

@tkhyn

This comment has been minimized.

Copy link

@tkhyn tkhyn commented Apr 25, 2016

Hi all, I've just run into this issue as well and found another simple workaround:

import {useView} from 'aurelia-templating';
import {AiDialog} from 'aurelia-dialog';

aurelia.use.plugin('aurelia-dialog', config => {
    useView('path/to/custom/view/ai-dialog.html')(AiDialog);
    config.useDefaults();
})

And in path/to/custom/view/ai-dialog.html:

<template>
  <require from="path/to/custom/css/dialog.css"></require>
  <content select="ai-dialog-header"></content>
  <content select="ai-dialog-body"></content>
  <content select="ai-dialog-footer"></content>
</template>

In my case I got rid of the require tag to simply use the CSS generated from SASS. This is cleaner than leaving the default generated CSS in the document and attempting to override it.

@arjanvanderleden

This comment has been minimized.

Copy link

@arjanvanderleden arjanvanderleden commented May 4, 2016

I'd go with the solution @jedd-ahyoung suggested. In fact an easy way to override some of the default css rules is to add a class to your body element and create more specific rules like so :

body.ci {
   ai-dialog {
      min-width: 500px; 
   }
}
@TylerJPresley

This comment has been minimized.

Copy link

@TylerJPresley TylerJPresley commented Jun 5, 2016

+1 for configurable css, or don't include it. Embedded styles are the worst. It's a 99.999999% guarantee that they won't match the site anyway. Having to go through the process to override the view seems a bit much. Just my 2 cents for the .00000000001 cents it's worth.

@TylerJPresley

This comment has been minimized.

Copy link

@TylerJPresley TylerJPresley commented Jun 5, 2016

Thanks @tkhyn this works. It feels hacky, but works. BTW it's
import {useView} from 'aurelia-framework';

@mttmccb

This comment has been minimized.

Copy link
Contributor

@mttmccb mttmccb commented Jun 5, 2016

Definitely hacky, should really be a config option, maybe optionally include default CSS?

@EisenbergEffect

This comment has been minimized.

Copy link
Member

@EisenbergEffect EisenbergEffect commented Jun 18, 2016

New api provided to supply a custom style sheet during configuration.

@jfstephe

This comment has been minimized.

Copy link

@jfstephe jfstephe commented Jul 11, 2018

@EisenbergEffect - can you provide a link to that config option? I can't see it in the docs (but could be missing something!).

I too need to be able to configure ux-dialog-container and below.

@StrahilKazlachev

This comment has been minimized.

Copy link
Contributor

@StrahilKazlachev StrahilKazlachev commented Jul 11, 2018

@jfstephe to disable the default styling call .useCSS(""); - on the dialog config object. The argument is typed as string, but if you are not using TS you can pass any falsy value.

@jfstephe

This comment has been minimized.

Copy link

@jfstephe jfstephe commented Jul 11, 2018

@StrahilKazlachev - thanks for that - I can't find any documentation on the aurelia site for 'useCSS'. Where did you find information about that from? Do I provide a css file location or the css itself? Does this apply to the ux-dialog-container or only ux-dialog?

@StrahilKazlachev

This comment has been minimized.

Copy link
Contributor

@StrahilKazlachev StrahilKazlachev commented Jul 11, 2018

@jfstephe no idea why the API docs are missing, did ping the right person about that though.

  1. .useCSS is basically a wrapper around DOM.injectStyles, so the argument is just the content of a .css file.
  2. It completely replaces(never get injected) the default styles. There is no scoping.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Linked pull requests

Successfully merging a pull request may close this issue.

None yet
You can’t perform that action at this time.