Science1 publisher2 min readPublished
ThinkR's CSS colour scraper ranked its own brand orange behind the body-text grey
The R package {css2r} takes a URL, reads the site's stylesheets and hands back a bslib theme in seconds. On its authors' own homepage the theme came back with a dark slate grey as primary, because that hex code appears 133 times.
The Scientist · Science desk

What happened
- ThinkR published an R package, {css2r}, that takes a website URL, downloads its stylesheets and returns a ready-made bslib::bs_theme() call for a Shiny app.
- The colour extraction is five lines of code built on one regular expression: find every six-digit hex code in the CSS text, count how often each appears, and sort.
- Run on the authors' own site, the tool put the ThinkR orange #F05622 third with 40 occurrences and left it out of the generated theme entirely.
- Background and foreground are assigned by a relative luminance calculation, with the lightest of the candidate colours becoming bg and the darkest becoming fg.
- A hosted Shiny wrapper does the same job through a form, showing swatches, a copy button and a live Bootstrap mockup of the resulting theme.
Compiled by The ScientistSomething wrong?How this is made
Why it matters
- capability A team with no brand documentation to hand can now pull a usable palette straight off its own public site, without anyone opening the brand book first.
- constraint Ranking by how often a hex code appears means a colour used sparingly but prominently loses to one declared in every text rule, so the primary slot needs a human check on every run.
- decision Anyone wiring this into an automated theming step has to decide whether the output is a draft for editing or a result to trust; on the authors' own site it was a draft.
The 133 is a count of string matches in downloaded stylesheet text. #38404C is the body-text colour on thinkr.fr, and body text gets declared in a lot of rules [12]. The orange on the ThinkR logo gets declared where the logo and a few buttons are [11]. The grey's hex code appears about 3.3 times as often as the orange, and the orange accounts for roughly 15 percent of the 268 occurrences counted across the four ranked colours [1][4].
Background and foreground come from a luminance function that divides each channel by 255 and weights red, green and blue at 0.2126, 0.7152 and 0.0722 [6]. The lightest candidate becomes bg and the darkest becomes fg [7]. Run the function by hand and #38404C scores about 0.248 while the brand blue #0046C8 scores about 0.253 [2], a gap of roughly 0.005 that decided which colour became the foreground. The orange scores about 0.451 and the cyan #20B8D6 about 0.603 [3]. The generated theme set bg to #FFFFFF, a code the scan found once in the CSS [9][10][8].
The post does not dress this up. "We ran our own tool on our own website and it did not find our own brand colour," the ThinkR authors wrote [13].
The extraction step is five lines built on the pattern #[0-9A-Fa-f]{6} [4]. Colours written as three-digit shorthand, or as rgb(), rgba() or hsl(), are never counted [5]. The link filter keeps only stylesheets on the same domain as the page [3], so a brand stylesheet served from another host is not downloaded at all [6]. Extraction reads <link rel="stylesheet"> elements, which leaves colours set in an inline <style> block or a style attribute outside the corpus [7]. Font detection works by reading Google Fonts URL parameters [14], so a self-hosted typeface does not register; the thinkr.fr run reported no Google Fonts [14].
What lands in your hands after a few seconds is still useful. The object holds top_colors as a colour-and-count data frame and shiny_code as a fluidPage call you can paste into app_ui.R [16], and the hosted app adds swatches, a copy button and a live Bootstrap mockup of the result [15]. On thinkr.fr the brand orange is the third row of that printed table, even though it is missing from the bs_theme() call the tool wrote [8][11].
What to watch
- Whether extract_colors() gains patterns for rgb(), hsl() and three-digit shorthand, which would change which colour ranks first on many sites.
- Whether the ranking step starts weighting colours by where they are declared, rather than counting every occurrence equally.
- Whether the same-domain link filter becomes optional, so stylesheets served from another host can be included.