Repository: netbox-community/netbox

Issue:
New dashboard widgets have insufficient header contrast in dark mode
### NetBox version

v3.5.0-dev

### Python version

3.8

### Steps to Reproduce

1. Navigate to the dashboard screen with the new widgets.

### Expected Behavior

The text and icon colors within the card header should maintain at least the same contrast ratio between light and dark modes. You can see this done within the search and log in buttons in the screenshots below.

### Observed Behavior

Currently, the colors used for the text and icons for card headers in dark mode do not provide much contrast against the background, making them very difficult to see. Screenshots below for reference.

![beta-demo netbox dev_light](https://user-images.githubusercontent.com/24281183/231177958-95e85747-d315-48d9-9738-1c5b4acab55e.png)
![beta-demo netbox dev_dark](https://user-images.githubusercontent.com/24281183/231177957-b4edc302-60f1-4842-b931-8bea66a1a37c.png)


Discussion:
For the lighter colors this happens:
![image](https://user-images.githubusercontent.com/91552302/231199538-638cbb65-e460-400d-9d2e-adffc470c730.png)

Could this be fixed the same way the #8088 was?
I don't think we'll be able to use `foreground_color()` directly, because it takes a discrete RGB value, whereas in this case we're working with named colors (e.g. `blue` vs. `#0000ff`). But we should be able to come up with a similar approach.
Just playing around with this a bit in Dev Tools, I changed the `text-light` class to `text-black` for the text and `text-gray` to `text-black-50` for the icons and it certainly puts the contrast for the text at a good level. At 50% opacity though, the icons are still a little low on contrast, but at 100%, I think they have too much contrast (example on *NetBox News* widget). Those were the only out-of-the-box Bootstrap color classes that I saw available to try in the black space, but I haven't had a chance to look into the code to see if changing the classes like that between themes is even a viable option. Screenshot for reference:

![beta-demo netbox dev_dark_black2](https://user-images.githubusercontent.com/24281183/231497801-507b3815-3d79-4eb8-997c-38816f023266.png)

Actually, just removing the class `bg-secondary` on the `card-header` div makes this much more readable and looks a bit more like the original ui:
![image](https://user-images.githubusercontent.com/1564902/234748366-6f06b9ab-3ba0-43b6-939f-ea93b106cded.png)

The _lighter_ version still needs it or at least the text and icons need a different class:
![image](https://user-images.githubusercontent.com/1564902/234748754-12aae83a-c6e7-48ec-926c-3dcb3c24014d.png)

Same with `text-dark` applied:
![image](https://user-images.githubusercontent.com/1564902/234749383-4ab10b40-4abe-499a-a351-b424e2dad421.png)

If the proposed solution is accepted I can take ownership of this.

I much prefer keeping the card headers. IMO it looks better and the ability to select different colors helps provide visual clues.
It wouldn't change the ability to change colors, only change the default gray looking one @jeremystretch .
This is really an issue, because even for the demo site we have either:

- Barely readable Default color in light mode (default color prior to v3.5)
- Barely readable Gray color in dark mode (default color since v3.5)

Resolving patch:
diff --git a/netbox/extras/dashboard/widgets.py b/netbox/extras/dashboard/widgets.py
--- a/netbox/extras/dashboard/widgets.py
+++ b/netbox/extras/dashboard/widgets.py
@@ -16,6 +16,7 @@
 
 from extras.choices import BookmarkOrderingChoices
 from extras.utils import FeatureQuery
+from utilities.choices import ButtonColorChoices
 from utilities.forms import BootstrapMixin
 from utilities.permissions import get_permission_for_model
 from utilities.templatetags.builtins.filters import render_markdown
@@ -115,6 +116,22 @@ def render(self, request):
     def name(self):
         return f'{self.__class__.__module__.split(".")[0]}.{self.__class__.__name__}'
 
+    @property
+    def fg_color(self):
+        """
+        Return the appropriate foreground (text) color for the widget's color.
+        """
+        if self.color in (
+            ButtonColorChoices.CYAN,
+            ButtonColorChoices.GRAY,
+            ButtonColorChoices.GREY,
+            ButtonColorChoices.TEAL,
+            ButtonColorChoices.WHITE,
+            ButtonColorChoices.YELLOW,
+        ):
+            return ButtonColorChoices.BLACK
+        return ButtonColorChoices.WHITE
+
     @property
     def form_data(self):
         return {


Test patch: