We just shipped this pull request into Forem which allows forem admins to define the core brand color of a new Forem, and I thought it was worth sharing and talking about the approach.
This feature allows admins to set their own main brand color....
This color needs to contrast properly with white, so I added the wcag contrast gem. I think this is a good gem to add, because we can start using this elsewhere, like user profile colors, tags, etc. Currently we let users set non-contrasting colors and basically just moderate it. This will help us ensure a minimum of 4.5:1 contrast for readability and accessibility for all.
The gem has not recently been updates, but it is a fairly simple low-bloat utility gem. The gem author is active and I feel like we can be comfortable that we could merge a change if we needed to.
Also added some basic validations in the controller. I figured we can start adding some validations in this area and extract these to a better place in a future refactor once we establish more of these.
If you're not aware, Forem is the open source platform that powers DEV, a few other communities, and we will be launching much more functionality to support a broad array of communities in the coming weeks and months, getting closer to the infrastructure generalization which will make this possible en masse. Along the way we continue to make improvements to the tools available to help each forem succeed on their own terms, and exist within a diverse, but cohesive ecosystem.
In building out our customization of the software, we are trying to be gradual in our approach. It's much easier to add a customization feature than to take it away later, and we don't want to get to the point where Forems are over-stuffed with features and have degrading performance.
So with that in mind, the most recent functionality that shipped is one which allows Forem admins to designate their primary brand color— which is used across the platform on buttons, and call-to-actions.
Server side implementation
We use a cached settings Ruby gem to implement the whole suite of site-wide options that admins can vary....
This is improved from rails-settings, added caching for all settings
Settings Gem
This is improved from rails-settings
added caching for all settings. Settings is a plugin that makes managing a table of
global key, value pairs easy. Think of it like a global Hash stored in your database
that uses simple ActiveRecord like methods for manipulation. Keep track of any global
setting that you dont want to hard code into your rails app. You can store any kind
of object. Strings, numbers, arrays, or any object.
But there are more concerns than this on the server side, we need to validate that value matched the proper hex pattern of /\A#(\h{6}|\h{3})\z/. This was pretty straightforward and can be seen in the pull request. The more interesting part was validating for an accessible color contrast.
In the current functionality, the color needs to be dark enough to contrast against white (e.g. the button text color), so we imported a gem for that...
In order to simplify our interface and ensure that we don't need to change too much in the code should this dependency become unreliable in the future, we also established a Color::Accessibility object which can be used wherever we need it.
Further uses of this could be validations on user-defined profile colors. Currently we have some functionality to try and ensure contrast, but it is a poor implementation and we cannot guarantee the WCAG standard of 4.5:1 contrast ratio on profile colors.
Front end implementation
The CSS required to make this work is quite minimal... because of the great work that had gone into our CSS system prior to this functionality. We have already established a set of CSS variables used throughout the site, so that we only have to change a few lines in the application and everything else should fall in line.
This snippet was added directly into the html.erb file which is inserted into the head of the document...
And that is all it takes for the rest of the CSS to respect the --accent-brand variables throughout. Check out the above pull request to take a peak at all the code and conversation that went into this change.