Repository: evidentlyai/evidently
Issue: Pleasant Default Colour Schema
GitHub labels: enhancement

I'd propose to include some smart / pleasant default colour schema as options that users could have at their fingertips, saving them looking up colour codes and assembling something that looks ok.

I'd be happy to implement this as a PR, if you'd be interested.

That sounds awesome, we’d be very happy to have it! 

However, there is a thing I want to highlight. Together with https://github.com/evidentlyai/evidently/tree/main/src/evidently module, where we implement most of the package logic, we have https://github.com/evidentlyai/evidently/tree/main/ui part as well. Significant share of the visuals is specified there and it might be a bit difficult to customize it.
Honestly, that is a reason why we still did not support a Dark Scheme for the reports: the background colors is specified in UI and we do not have one particular place where we can specify it :(

Taking this into account I believe it will be much more convenient to implement default colour schemas on top of the available color constants: https://docs.evidentlyai.com/user-guide/customization/options-for-color-schema#available-options.

If you are willing to touch this UI part, you are very welcome too! Just wanted to mention that it might bring extra complexity.


Probably the easiest way to implement some of these would be to do a series of `ColorOptions` definitions within the `color_scheme.py` file itself, perhaps like this:

```python
# ... all the definitions of `ColorOptions` etc above...

def _set_color_options(_self, **kwargs):
    for k, v in kwargs.items():
        setattr(_self, k, v)


solarized_color_options = ColorOptions()

_set_color_options(
    solarized_color_options,
    primary_color="#268bd2",
    secondary_color="#073642",
    current_data_color="#268bd2",
    reference_data_color="#073642",
    color_sequence=[
        "#268bd2",
        "#2aa198",
        "#859900",
        "#b58900",
        "#cb4b16",
        "#dc322f",
    ],
)
```

I have maybe 5 or 10 different color schemes in mind that I could define this way. This way the user can just import them  directly without having to define all the attributes.

I guess I'd update the docs, too, to reflect this change and show how people can use it.

Hi @strickvl,

Yes it is good way to create predefined `ColorOptions` but you don't even need `_set_color_options` as you can write it as:

```python
solarized_color_options  = ColorOptions(primary_color="#268bd2",
    secondary_color="#073642",
    current_data_color="#268bd2",
    reference_data_color="#073642",
    color_sequence=[
        "#268bd2",
        "#2aa198",
        "#859900",
        "#b58900",
        "#cb4b16",
        "#dc322f",
    ],
)
```
This would work the same, and if you skip some of fields it would use default one.

@Liraim Thanks. Yeah I had actually thought I could just set it directly, but I was hitting some errors around that. I'll take another look later on and see if I can implement this one way or another.

If you need help why something doesn't work as you thought, you can ask here or in Discord.

I tried it the way you suggested, but I get the following error when trying to instantiate the object:

```
---------------------------------------------------------------------------
TypeError                                 Traceback (most recent call last)
/Users/strickvl/coding/evidently/test.ipynb Cell 1' in <cell line: 1>()
----> 1 from evidently.options import ColorOptions
      2 from evidently.dashboard import Dashboard
      3 from evidently.dashboard.tabs import DataDriftTab

File ~/coding/evidently/src/evidently/options/__init__.py:3, in <module>
      1 from typing import TypeVar, Generic, Type, Dict, Any
----> 3 from .color_scheme import ColorOptions, solarized_color_options
      4 from .data_drift import DataDriftOptions
      5 from .quality_metrics import QualityMetricsOptions

File ~/coding/evidently/src/evidently/options/color_scheme.py:75, in <module>
     49         return self.reference_data_color or self.secondary_color
     52 # def _set_color_options(_self, **kwargs):
     53 #     for k, v in kwargs.items():
     54 #         setattr(_self, k, v)
   (...)
     72 #     ],
     73 # )
---> 75 solarized_color_options = ColorOptions(
     76     primary_color="#268bd2",
     77     secondary_color="#073642",
     78     current_data_color="#268bd2",
     79     reference_data_color="#073642",
     80     color_sequence=[
     81         "#268bd2",
     82         "#2aa198",
     83         "#859900",
     84         "#b58900",
     85         "#cb4b16",
     86         "#dc322f",
     87     ],
     88 )

TypeError: __init__() got an unexpected keyword argument 'primary_color'
```

This is the reason I decided to create a small helper method, @Liraim.

Oh, I've missed that: there are no type hints for fields in `ColorOptions` class so `dataclass` do not create `__init__` method with them. Probably, best solution would be add type hints for fields.

Something like this:
```python
@dataclass
class ColorOptions:
    primary_color: str = RED
    secondary_color: str = GREY
    current_data_color: Optional[str] = None
    reference_data_color: Optional[str] = None
    color_sequence: List[str] = COLOR_DISCRETE_SEQUENCE
    fill_color: str = "LightGreen"
    zero_line_color: str = "green"
    non_visible_color: str = "white"
    underestimation_color: str = "#6574f7"
    overestimation_color: str = "#ee5540"
    majority_color: str = "#1acc98"
 ```

Ah yes, you're right. The only part that remains a bit tricky is using a mutable default value. For `color_sequence` it seems I have to do something like this:

```python
color_sequence: list = field(
        default_factory=lambda: COLOR_DISCRETE_SEQUENCE
    )
```

but on instantiating the object I get this error:

```
TypeError: 'list' object is not callable
```

You can change  COLOR_DISCRETE_SEQUENCE from list to tuple (immutable), so you will be able to use it directly as before. 
```python
COLOR_DISCRETE_SEQUENCE = ("#ed0400", "#0a5f38", "#6c3461", "#71aa34", "#d8dcd6", "#6b8ba4")
```