Today we’re going to talk about a limited “generative system” that “writes” CSS code though I wouldn’t call this a ‘bot’.
What this generator does is “explore” the existing possibilities of a predefined problem space using known ranges and random selection. 😉
I wrote some of my thoughts regarding “generative systems” in relation to programmers and software developers in my article The Death of the Programmer and I also implemented a generative “writer bot” that I trained and used to write my article A Halloween Tale.
However in those cases I was mainly referring to semi-intelligent (in regard to the problem space) systems (“bots”) that are able to make choices on their own their own, and or are capable of learning, evolving or growing in some capacity based on input or state but that is not what we are doing today.
This is a simple generative tool (the Button CSS generator I provide below) is useful but it has no knowledge of what looks good (or bad) and no way to learn them either!
It can only provide you with options but it is far from replacing you.
You can view a Live Preview: Here
Though in case you wanted it, here’s a screenshot:
There are only 3 files used to create the Button CSS generator: style.css, functions.js & index.html
Style.css
This is the CSS styles used by the index.html file. You will notice that there is a base button CSS template that all the buttons share when the page is first loaded.
The “hardcoded” styles are: text-align, text-decoration, display, padding, font-size.
Once the page is loaded however I use JavaScript to generate and apply additional CSS attributes which are responsible for the variations in each button that you see.
The style.css file is an external style sheet and is linked to using <link rel=”stylesheet” type=”text/css” href=”style.css”> in the HTML file.
h1{ width:100%; text-align: center; } button { /* hardcoded styles*/ text-align: center; text-decoration: none; display: inline-block; padding: 15px 32px; font-size: 1.5em; } table{ text-align: center; width:100%; } caption{ font-size:2em; } #randomize{ background-color: rgb(62, 176, 239); color: rgb(138, 219, 215); border-color: rgb(28, 65, 39); border-width: 11px; border-style: dotted; } #show-css-wrapper{ width: 100%; text-align: center; margin-left: auto; margin-right: auto; } #show-css-title{ width: 100%; text-align: center; margin-left: auto; margin-right: auto; background-color: #999999; } #show-css{ width: 40%; min-width: 420px; text-align: left; margin-left: auto; margin-right: auto; background-color: #eeeeee; } textarea{ width: 100%; height: 200px }
Functions.js
This file contains all the functions that the generator will use to make new button CSS styles.
There is some inline JavaScript in the HTML file that makes use of these functions and I will discuss that in the Index.html section below.
There is a lot of similarity between most of the functions and although much of their functionality can be wrapped up into one function I intentionally implemented the prototype generator this way.
I could say the reason is that I wanted to avoid early optimization but the real reason is that this way is just easier to read for someone who didn’t write the software and or is just learning to code.
Though in truth SetBackgroundColor(element) or SetBorderWidth(element) is not significantly more readable than a more optimized generic function like SetCSS(element, cssProperty, optional [ value ]) and if I was going to spend more time developing this system I would probably end up going that route because the generic function would be far easier to maintain in the long run simply because it would be one function to maintain instead of many.
The functions.js file is an external script and is linked to the HTML file using the “src” (source) attribute on a script tag.
//////////////////////////////////// // Random Button CSS Generator Functions // Will return 0 or 1 function CoinFlip(){ return Math.floor(Math.random() * 2); //0/1 } // Return a random string of RGB values e.g. "255, 255, 255" function RandomRGBColor(){ var r, g, b; r = Math.floor(Math.random() * 256); g = Math.floor(Math.random() * 256); b = Math.floor(Math.random() * 256); color = r.toString() + ', ' + g.toString() + ', ' + b.toString(); return color; } // Set SetBackgroundColor() function SetBackgroundColor(element, color = null){ // No color given so set to random if(color === null){ element.style.backgroundColor = 'rgb(' + RandomRGBColor() + ')'; }else{ // Set to provided color element.style.backgroundColor = 'rgb(' + color + ')'; } } // Set SetFontColor() function SetFontColor(element, color = null){ // No color given so set to random if(color === null){ element.style.color = 'rgb(' + RandomRGBColor() + ')'; }else{ // Set to provided color element.style.color = 'rgb(' + color + ')'; } } // Set SetBorderColor() function SetBorderColor(element, color = null){ // No color given so set to random if(color === null){ element.style.borderColor = 'rgb(' + RandomRGBColor() + ')'; }else{ // Set to provided color element.style.borderColor = 'rgb(' + color + ')'; } } // Set SetBorderWidth() function SetBorderWidth(element, width = null){ // No width given so set to random if(width === null){ element.style.borderWidth = Math.floor(Math.random() * 14).toString() +'px'; }else{ // Set to provided color element.style.borderWidth = width.toString() + 'px'; } } // Set SetBorderStyle() function SetBorderStyle(element, style = null){ // No width given so set to random if(style === null){ var styles = ['none', 'hidden', 'dotted', 'dashed', 'solid', 'double', 'groove', 'ridge', 'inset', 'outset', 'initial', 'inherit']; element.style.borderStyle = styles[Math.floor(Math.random()*styles.length)]; }else{ // Set to provided color element.style.borderStyle = style; } } // Get the CSS for the button that was clicked and show it on the page function ShowCSS(element){ var style = window.getComputedStyle(element); var backgroundColor = style.getPropertyValue('background-color'); var color = style.getPropertyValue('color'); var borderColor = style.getPropertyValue('border-color'); var borderWidth = style.getPropertyValue('border-width'); var borderStyle = style.getPropertyValue('border-style'); var padding = style.getPropertyValue('padding'); var textDecoration = style.getPropertyValue('text-decoration'); var display = style.getPropertyValue('display'); var fontSize = style.getPropertyValue('font-size'); var css = ''; css += 'button{\n'; css += ' background-color: ' + backgroundColor.toString() + ';\n'; css += ' color: ' + color.toString() + ';\n'; css += ' border-color: ' + borderColor.toString() + ';\n'; css += ' border-width: ' + borderWidth.toString() + ';\n'; css += ' border-style: ' + borderStyle.toString() + ';\n'; css += ' padding: ' + padding.toString() + ';\n'; css += ' text-decoration: ' + textDecoration.toString() + ';\n'; css += ' display: ' + display.toString() + ';\n'; css += ' font-size: ' + fontSize.toString() + ';\n'; css += '}\n'; document.getElementById('css-styles').innerHTML = css; }
Index.html
Index.html is the core that brings all the pieces of this software together!
You will notice that I used divisional elements (div tags) and a textarea to create the section at the top of the page where the CSS code is shown when you click one of the buttons.
Beneath that is the randomize button hyperlinked to nothing, followed by a table. Why a table and not a responsive grid? Eh… it was faster for the prototype mainly.
Inside each cell of the the table is a button that has an onclick attrabute that passes the element’s ID reference ‘this’ to the function ShowCSS().
Beyond this the only thing of consequence in the HTML file is the inline JavaScript that uses the code from the functions.js script to make everything work.
When the page loads I use an array of strings of element id’s (the buttons) to locate & establish references to the DOM objects for each button and store those references in the buttons array.
Then, for each of the element references in the buttons array I set a background color as well as a font color randomly. After that I “flip a coin” to decide if a boarder should be applied to the button. If yes, I apply a random boarder color, width and style.
Once the code is done running all the buttons will be randomly styled and are ready for you to click them to get the CSS if you like them or click Randomize to get a new set of random buttons.
<html> <head> <title>Random Button</title> <link rel="stylesheet" type="text/css" href="style.css"> </head> <body> <!-- Title --> <h1>Random Button CSS Generator</h1> <!-- Show CSS --> <div id="#show-css-wrapper"> <div id="show-css"> <h2 id="show-css-title">Button CSS</h2> <p>Select a button to view it's CSS styles</p> <textarea id="css-styles">CSS will show here</textarea> </div> </div> <!-- Table --> <table> <tr> <td></td> <td> <h3> <a href=""> <button id="randomize"> Randomize </button> </a> </h3> </td> <td></td> </tr> <tr> <td> </td> <td> </td> <td> </td> </tr> <tr> <td><button id="button-1" onclick="ShowCSS(this)">Button 1</button></td> <td><button id="button-2" onclick="ShowCSS(this)">Button 2</button></td> <td><button id="button-3" onclick="ShowCSS(this)">Button 3</button></td> </tr> <tr> <td><button id="button-4" onclick="ShowCSS(this)">Button 4</button></td>... <td><button id="button-5" onclick="ShowCSS(this)">Button 5</button></td> <td><button id="button-6" onclick="ShowCSS(this)">Button 6</button></td> </tr> <tr> <td><button id="button-7" onclick="ShowCSS(this)">Button 7</button></td> <td><button id="button-8" onclick="ShowCSS(this)">Button 8</button></td> <td><button id="button-9" onclick="ShowCSS(this)">Button 9</button></td> </tr> </table> <!-- Include the Functions --> <script src="functions.js"></script> <script> // Button Element ID's var elementsIDs = ['button-1', 'button-2', 'button-3', 'button-4', 'button-5', 'button-6', 'button-7', 'button-8', 'button-9']; var buttons = []; // Array to Store Button References // Setup Element References to the Buttons elementsIDs.forEach(element => { buttons.push(document.getElementById(element)); }); // Apply Random CSS to each Button buttons.forEach(element => { SetBackgroundColor(element); // Set Initial Background Colors SetFontColor(element); // Set Initial Font Colors if(CoinFlip() == 1){ // Has border? SetBorderColor(element); SetBorderWidth(element); SetBorderStyle(element); } }); </script> </body> </html>
Where to go from here
As a proof of concept I think I accomplished my goal of creating a functional Button CSS Generator however a more complete prototype would want to include features like allowing you to edit the CSS of the selected button and have the changes reflect on the button in real time or via an “update” button.
Also, perhaps the ability to “favorite” or save a button’s CSS style so that you can reload it again later or some kind of “history” feature would be nice so that you can keep the button styles you like.
Additionally, the CSS attributes the generator uses are hard coded and the property or attribute “min/max” ranges are set as well… A nice feature would be be to have the option to edit/add or remove the CSS fields included in the generator and modify the ranges as well.
It would also be nice for the prototype to allow for the generation of vertical and horizontal menus using the button CSS and include the ability to edit the links or “onclick” actions and the text of the buttons.
The “ultimate” implementation of a generator like this would allow you to generate styles for all or some subset of the HTML elements not just buttons.
I will leave all these features for you to add to your own implementations at this time however if you like this project I may just add some or all of these features in the future.
You can view a Live Preview: Here
You can get the code on GitHub: Here
You can find a list of all my other posts on my Topics and Posts page.
I hope you enjoyed reading this article.
Your financial support allows me to dedicate time to developing projects like this and while I am publishing them without cost, that isn’t to say they are free. I am doing this all by myself and it takes me a lot of time and effort to build and publish these projects for your enjoyment.
So I ask that if you like my content, please support me on Patreon for as little as $1 or more a month.
Feel free to suggest a project you would like to see built in the comments and if it sounds interesting it might just get built and featured here on my blog for you to enjoy.
Much Love,
~Joy
August 11, 2018 at 7:27 am
What will be next? A bot listening to human voice instructions and creating all the CSS rules for the design !
We already have number of page builders for WordPress which are creating css rules based on user choices. Some builders are so good that they are already making css experience obsolete and allowing new set of professionals who are expert in building pages using those page builders. True, death of the programmer is already started, although it is just the beginning.
LikeLiked by 1 person
August 11, 2018 at 10:40 pm
Haha, actually I do have an FFT (https://en.wikipedia.org/wiki/Fast_Fourier_transform) sitting half complete in my projects folder! 😛 I think the next step after a simple random generator like this is a system that implements evolution using progenitor elements with variations and “mutations” from the parent/s element’s “DNA”. This would offer an improved selection process as you evolve the element or code. I originally conceived this project with the idea of implementing an evolutionary algorithm but I didn’t have a ton of time to add features and I would really like to cover evolutionary algorithms in my Ancestor Simulations series if I can ever get back to it. Beyond that would be a semi-intelligent system that would generate mockups. You could track the “good” selections and over time teach the “bot” how to produce good content “autonomously” without the need for a programmer as is the case with the existing systems which require a developer to build a template or are rule based such as this generator is. Ideally, we should be developing systems that non-technical end users can interact with organically. Thanks for the great comment geekmahesh!
LikeLiked by 1 person