This article covers creating a Riot button component, using the Material Design CSS BeerCSS. Before starting, make sure you have a base application running, or read my previous article Setup Riot + BeerCSS + Vite.
These articles form a series focusing on RiotJS paired with BeerCSS, designed to guide you through creating components and mastering best practices for building production-ready applications. I assume you have a foundational understanding of Riot; however, feel free to refer to the documentation if needed: https://riot.js.org/documentation/
BeerCSS provides many button styles, such as "large", "outlined", "fill" and many more (see the following screenshot). The goal is to create a button component that dynamically supports all stylings and listens to click events.
Button Component Base
First, create a new file named c-button.riot
under the components folder. The c-
stands for "component", a useful naming convention and a good practice.
Into ./components/c-button.riot
, write the minimum HTML and code for a Button:
<c-button>
<button>
<slot></slot>
</button>
</c-button>
Let's break down the code:
- The
<c-button>
and</c-button>
defined a custom root tag, with the same name as the file. You must write it; otherwise, it may create unexpected results. Using only or redefining native HTML tags is a bad practice, so starting c- is a good naming. - To print a custom Button name, a Slot
<slot></slot>
is used: The<slot>
tag is a special Riot.js core feature that allows you to inject and compile the content of any custom component inside its template in runtime.
Then we can load and instantiate the c-button.riot, into a front page index.riot:
<index-riot>
<div style="width:600px;padding:20px;">
<h4 style="margin-bottom:20px">Riot + BeerCSS</h4>
<c-button onclick={ clicked }>Home</c-button>
</div>
<script>
import cButton from "./components/c-button.riot";
export default {
components: {
cButton
},
clicked () {
console.log("clicked");
}
}
</script>
</index-riot>
Code break-down:
- The component is imported with import cButton from "../components/c-button.riot" then loaded into the components:{} Riot object.
- On the HTML, the Button component is instantiated with
<c-button onclick={ clicked }>Home</c-button>
- The event click is listened thanks to the onclick attribute: when the event is fired, the function clicked is executed.
Screenshot of the generated HTML with the custom button:
With the simplest button, we can play by creating a counter. One value increased or decreased based on the button click, here are the HTML and Javascript on the index.riot file:
<index-riot>
<div style="width:600px;padding:20px;">
<h4 style="margin-bottom:20px">Riot + BeerCSS</h4>
<c-button onclick={ add }>Add</c-button-test>
<c-button>{state.number}</c-button-test>
<c-button onclick={ sub }>Sub</c-button-test>
</div>
<script>
import cButton from "./components/c-button.riot";
export default {
components: {
cButton
},
state: {
number: 0
},
add () {
this.update({ number: this.state.number + 1 })
},
sub () {
this.update({ number: this.state.number - 1 })
}
}
</script>
</index-riot>
Code Breakdown:
- To store the number value, it must be declared into the state: {} object.
- If the button Add is clicked, it will execute the add() function to add one to the number.
- If the button Sub is clicked, it will execute the sub() function to subtract the number by one.
Here is the generated HTML of the small counter:
It is not necessary to create a add
and sub
function; the counting can be simplified in one line for each button with onclick={ () => update({ number: state.number + 1 }) }. The code for index.riot :
<index-riot>
<div style="width:600px;padding:20px;">
<h4 style="margin-bottom:20px">Riot + BeerCSS</h4>
<c-button onclick={ () => update({ number: state.number + 1 }) }>Add</c-button-test>
<c-button >{state.number}</c-button-test>
<c-button onclick={ () => update({number: state.number - 1}) }>Sub</c-button-test>
</div>
<script>
import cButton from "./components/c-button.riot";
export default {
components: {
cButton
},
state: {
number: 0
}
}
</script>
</index-riot>
Button Component Styles
Let's add a custom icon to the c-button.riot
:
<c-button>
<button>
<i if={ props?.icon }> {props.icon} </i>
<slot></slot>
</button>
</c-button>
If the props icon exists (HTML attribute), the <i>
tag will be displayed with the icon symbol.
BeerCSS supports Material Fonts by default, here is the list of all icons: https://fonts.google.com/
Let's try the icon by defining the attribute "icon" with a symbol name into our index.riot, such as:
<c-button-test icon="add" onclick={ add }>Add</c-button-test>
<c-button-test>{state.number}</c-button-test>
<c-button-test icon="remove" onclick={ sub }>Sub</c-button-test>
The generated HTML prints the add and minus (named removed) symbols:
To create colour variations, we can add classes based on conditions: the following condition checks if the props.primary, props.secondary, or props.tiertiary exists, then it will add classes "primary", "secondary", or "tertiary" respectively.
<c-button>
<button class="
{ props?.primary ? " primary" : null }
{ props?.secondary ? " secondary" : null }
{ props?.tertiary ? " tertiary" : null }
">
<i if={ props?.icon }> {props.icon} </i>
<slot></slot>
</button>
</c-button>
Now we can try each attribute color on the index.riot:
<c-button-test primary="true" icon="add" onclick={ add }>Add</c-button-test>
<c-button-test secondary="true">{state.number}</c-button-test>
<c-button-test tertiary="true" icon="remove" onclick={ sub }>Sub</c-button-test>
The generated HTML displays three color variations:
The logic for supporting all +30 variations (squared, circle, shadow elevation, etc.) is the same: Apply a class if the attribute exists.
The result of mixed button variation with Riot and BeerCSS:
The code of the Button component is available on Github:
https://github.com/steevepay/riot-beercss/blob/main/components/c-button.riot
Button Component Testing
It exist two methods for testing the Button component, and it is covered in two different articles:
Conclusion
Voilà 🎉 We created a Button Riot Component using Material Design elements with BeerCSS.
Feel free to comment if you have questions or need help about RiotJS.
Have a great day! Cheers 🍻