B4J Question [BANAno]: [SOLVED] How can I add dynamic css / InjectCSS to my app?

Mashiane

Expert
Licensed User
Longtime User
Hi there.

I'm thinking of enabling users apply dynamic theming depending on the color and intensity then specify for their app. What this intends to do is basically take the 'backcolor' and apply it to all the controls so that one has the same color specs throughout.

The default colors for Materialize CSS 1.00 is teal and some variant of red. Going through their css, I have managed to pick up that setting a few backcolors this is able to be done. So I basically need something like InjectCSS on the _Ready function call.

Here is some kind of hypothetical example...

B4X:
'apply this theme based on app theme for all components
'set the mater theme
Sub ApplyTheme()
    If Theme.Length = 0 Then Return
    If Themes.ThemeExist(Theme) = False And Theme.Length > 0 Then
        LogError($"App: ${Theme} theme '${Theme}' DOES NOT exist!"$)
    End If
    Dim fColor As String = Themes.GetForeColorHex(Theme)
    Dim bColor As String = Themes.GetBackGroundColorHex(Theme)
    If bColor.length > 0 Then
        'forecolor
        page.AddCssRule($".pagination li.active"$, $"background-color: ${bColor};"$)
        page.AddCssRule($".pagination li.active a"$, $"color: ${bColor};"$)
        page.AddCssRule($"blockquote"$,$"border-left: 5px solid ${bColor}"$)
        page.AddCssRule($".btn:focus, .btn-large:focus, .btn-small:focus,.btn-floating:focus"$,$"background-color: ${bColor}"$)
        page.AddCssRule($".btn, .btn-large, .btn-small"$,$"background-color: ${bColor}"$)
        page.AddCssRule($".btn:hover, .btn-large:hover, .btn-small:hover"$,$"background-color: ${bColor}"$)
        page.AddCssRule($".btn-floating, .btn-floating:hover, .fixed-action-btn .fab-backdrop"$,$"background-color: ${bColor}"$)
        
        page.AddCssRule($".dropdown-content li>a, .dropdown-content li>span"$,$"color: ${bColor}"$)
        page.AddCssRule($"select:focus"$,$"outline: 1px solid ${bColor}"$)
        page.AddCssRule($"button:focus"$,$"background-color: ${bColor}"$)
        page.AddCssRule($".input-field .prefix.active"$,$"color: ${bColor}"$)
        page.AddCssRule($"[type="radio"]:checked+span:after"$,$"border: 2px solid ${bColor}"$)
        page.AddCssRule($"[type="radio"].with-gap:checked+span:before"$,$"border: 2px solid ${bColor}"$)
        page.AddCssRule($"[type="radio"].with-gap:checked+span:after"$,$"border: 2px solid ${bColor}"$)
        page.AddCssRule($"[type="radio"]:checked+span:after"$,$"background-color: ${bColor}"$)
        page.AddCssRule($"[type="radio"].with-gap:checked+span:after"$,$"background-color: ${bColor}"$)
        page.AddCssRule($"[type="checkbox"]:checked+span:not(.lever):before"$,$"border-right: 2px solid ${bColor};border-bottom: 2px solid ${bColor}"$)
        page.AddCssRule($"[type="checkbox"]:indeterminate+span:not(.lever):before"$,$"border-right: 2px solid ${bColor};"$)
        page.AddCssRule($"[type="checkbox"].filled-in:checked+span:not(.lever):after"$,$"border: 2px solid ${bColor};background-color: ${bColor};"$)
        page.AddCssRule($"[type="checkbox"].filled-in.tabbed:checked:focus+span:not(.lever):after"$,$"background-color: ${bColor};border-color: ${bColor};"$)
        page.AddCssRule($".switch label input[type=checkbox]:checked+.lever:after"$,$"background-color: ${bColor}"$)
        page.AddCssRule($".switch label input[type=checkbox]:checked+.lever"$,$"background-color: ${bColor}"$)
        page.AddCssRule($".select-wrapper input.select-dropdown:focus"$,$"border-bottom: 1px solid ${bColor}"$)
        page.AddCssRule($"input[type=range]+.thumb"$,$"background-color: ${bColor};"$)
        page.AddCssRule($"input[type=range]::-webkit-slider-thumb"$,$"background: ${bColor};background-color: ${bColor};"$)
        page.AddCssRule($"input[type=range]::-moz-range-thumb"$, $"background: ${bColor};"$)
        page.AddCssRule($"input[type=range]::-ms-thumb"$,$"background: ${bColor};"$)
        page.AddCssRule($".sidenav li>a.btn-floating:hover"$,$"background-color: ${bColor}"$)
        page.AddCssRule(".sidenav li>a.btn:hover, .sidenav li>a.btn-large:hover,.sidenav li>a.btn-small:hover,.sidenav li>a.btn-large:hover",$"background-color: ${bColor}"$)
        page.AddCssRule($".secondary-content"$,$"color: ${bColor}"$)
        page.AddCssRule(".collection .collection-item.active",$"background-color: ${bColor};"$)
        page.AddCssRule(".collection a.collection-item",$"color: ${bColor}"$)
        page.AddCssRule(".progress .determinate",$"background-color: ${bColor}"$)
        page.AddCssRule(".progress .indeterminate",$"background-color: ${bColor}"$)
        page.AddCssRule("span.badge.new",$"background-color: ${bColor}"$)
        
        page.AddCssRule(".chip",$"background-color: ${bColor}"$)
        page.AddCssRule(".input-field .prefix.active",$"color: ${bColor}"$)
        page.AddCssRule(".datepicker-date-display",$"background-color: ${bColor}"$)
        page.AddCssRule(".datepicker-table td.is-today",$"color: ${bColor}"$)
        page.AddCssRule(".datepicker-table td.is-selected",$"background-color: ${bColor}"$)
        page.AddCssRule(".datepicker-day-button:focus",$"background-color: ${bColor}"$)
        page.AddCssRule($".datepicker-cancel, .datepicker-clear, .datepicker-today,    .datepicker-done"$,$"color: ${bColor}"$)
        page.AddCssRule(".timepicker-digital-display",$"background-color: ${bColor}"$)
        page.AddCssRule(".timepicker-tick.active, .timepicker-tick:hover",$"background-color: ${bColor}"$)
        page.AddCssRule(".timepicker-canvas line",$"stroke: ${bColor}"$)
        page.AddCssRule(".timepicker-canvas-bearing",$"fill: ${bColor}"$)
        page.AddCssRule(".timepicker-canvas-bg",$"fill: ${bColor}"$)
        page.AddCssRule(".timepicker-close",$"color: ${bColor}"$)
        page.AddCssRule(".card .card-action a:not(.btn):not(.btn-large):not(.btn-small):not(.btn-large):not(.btn-floating)",$"color: ${bColor}"$)
        page.AddCssRule(".card .card-action a:not(.btn):not(.btn-large):not(.btn-small):not(.btn-large):not(.btn-floating):hover",$"color: ${bColor}"$)
        
        page.AddCssRule(".chip:focus",$"background-color: ${bColor}"$)
        page.AddCssRule(".chips.focus",$"border-bottom: 1px solid ${bColor}; -webkit-box-shadow: 0 1px 0 0 ${bColor}; box-shadow: 0 1px 0 0 ${bColor}"$)
        page.AddCssRule($".noUi-horizontal .noUi-handle, .noUi-vertical .noUi-handle"$,$"background-color: ${bColor}"$)
        page.AddCssRule($".noUi-connect"$,$"background: ${bColor}"$)
        page.AddCssRule($".noUi-target.noUi-horizontal .noUi-tooltip"$,$"background-color: ${bColor}"$)
        page.AddCssRule($".noUi-target.noUi-vertical .noUi-tooltip"$,$"background-color: ${bColor}"$)
        page.AddCssRule($".slider .indicators .indicator-item.active"$,$"background-color: ${bColor}"$)
        page.AddCssRule($".table-of-contents a.active"$,$"border-left: 2px solid ${bColor}"$)
        page.AddCssRule($".table-of-contents a:hover"$,$"border-left: 1px solid ${bColor}"$)
        page.AddCssRule($".tabs .tab a"$,$"color: ${bColor}"$)
        page.AddCssRule($".tabs .tab a:focus, .tabs .tab a:focus.active"$,$"background-color: transparent"$)
        page.AddCssRule($".tabs .tab a:hover, .tabs .tab a.active"$,$"color: ${bColor};"$)
        page.AddCssRule($".tabs .tab.disabled a, .tabs .tab.disabled a:hover"$,$"color: ${bColor};"$)
        page.AddCssRule($".tabs .indicator"$,$"background-color: ${bColor};"$)
        page.AddCssRule($".material-tooltip"$,$"background-color: ${bColor};"$)
        page.AddCssRule($"input:not([type]):focus:not([readonly]),
input[type=text]:not(.browser-default):focus:not([readonly]),
input[type=password]:not(.browser-default):focus:not([readonly]),
input[type=email]:not(.browser-default):focus:not([readonly]),
input[type=url]:not(.browser-default):focus:not([readonly]),
input[type=time]:not(.browser-default):focus:not([readonly]),
input[type=date]:not(.browser-default):focus:not([readonly]),
input[type=datetime]:not(.browser-default):focus:not([readonly]),
input[type=datetime-local]:not(.browser-default):focus:not([readonly]),
input[type=tel]:not(.browser-default):focus:not([readonly]),
input[type=number]:not(.browser-default):focus:not([readonly]),
input[type=search]:not(.browser-default):focus:not([readonly]),
textarea.materialize-textarea:focus:not([readonly])"$,$"border-bottom: 1px solid ${bColor}; -webkit-box-shadow: 0 1px 0 0 ${bColor}; box-shadow: 0 1px 0 0 ${bColor}"$)
    End If
    page.AddCssRule($"input:not([type]):focus:not([readonly])+label,
input[type=text]:not(.browser-default):focus:not([readonly])+label,
input[type=password]:not(.browser-default):focus:not([readonly])+label,
input[type=email]:not(.browser-default):focus:not([readonly])+label,
input[type=url]:not(.browser-default):focus:not([readonly])+label,
input[type=time]:not(.browser-default):focus:not([readonly])+label,
input[type=date]:not(.browser-default):focus:not([readonly])+label,
input[type=datetime]:not(.browser-default):focus:not([readonly])+label,
input[type=datetime-local]:not(.browser-default):focus:not([readonly])+label,
input[type=tel]:not(.browser-default):focus:not([readonly])+label,
input[type=number]:not(.browser-default):focus:not([readonly])+label,
input[type=search]:not(.browser-default):focus:not([readonly])+label,
textarea.materialize-textarea:focus:not([readonly])+label"$,$"color: ${bColor}"$)

    page.AddCssRule($".input-field>label"$,$"color: ${bColor};"$)
End Sub

Thanks.
 

Mashiane

Expert
Licensed User
Longtime User
I know from experience from ABM, you will end-up with a couple of thousands of those AddCSSRule lines. Materialize CSS is VERY badly prepared for theming.
Yeah hey. I wish I wouldn't have to do this. Fortunately if I apply the code above only once it changes everything so if I want everything to have a lightblue color, all textboxes, radio, buttons will have lightblue and not the teal and red they have. This will definitely not have extensive theming like ABM at component level at all, thus this global approach. Thanks for the heads up!
 
Upvote 0

alwaysbusy

Expert
Licensed User
Longtime User
not have extensive theming like ABM at component level
Good luck with that! I know a certain member on this forum who was the first to ask for ABM how he could change a border, forecolor, have multiple input colors, etc :rolleyes:

Just saying, maybe this is a good time to reflect on maybe a better system than I had to do in ABM. At first, I also thought everyone would've been happy with just a couple of colors, hence I wrote the .Colorize() method on the themes where one could do exactly what you want to do now, remember? But hell no, the users insisted in being able to do more. ;) Even up to today, so many years later, I still am working my ass off because of requests in the ABM feedback app for certain styling properties. And the thing with CSS is, the inheritance is a nightmare so it becomes increasingly difficult to keep track...
 
Upvote 0

Mashiane

Expert
Licensed User
Longtime User
Finally google found me an answer. This CSS DOM and HTML DOM exploration is yielding results.

Here we go..

Defining the CSS rule
B4X:
var sheet = document.createElement("style")
    sheet.innerHTML = "div {border: 2px solid black; background-color: blue;}"
    body.appendChild(sheet)

Adding the component...

B4X:
var div = document.createElement("div")
    div.textContent = "Testing dynamic styles"
    body.appendChild(div)

Output:

1600900870785.png


Wow! but then again, I no longer have a need for this.

Source: https://www.w3.org/wiki/Dynamic_style_-_manipulating_CSS_with_JavaScript
 
Upvote 0
Top